Pad a short RSA puzzle answer instead of failing the connect
An answer with a leading zero byte comes out of toByteArray() as 63 bytes, and copying it started one byte too far, failing about one connect in 256. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+14
-10
@@ -159,16 +159,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
|
|||||||
BigInteger x = new BigInteger(1, serverReplyStep3.getX());
|
BigInteger x = new BigInteger(1, serverReplyStep3.getX());
|
||||||
BigInteger n = new BigInteger(1, serverReplyStep3.getN());
|
BigInteger n = new BigInteger(1, serverReplyStep3.getN());
|
||||||
|
|
||||||
byte[] y = new byte[64];
|
byte[] y = unsigned64(x.modPow(BigInteger.valueOf(2L).pow(serverReplyStep3.getLevel()), n));
|
||||||
|
|
||||||
byte[] solution =
|
|
||||||
x.modPow(BigInteger.valueOf(2L).pow(serverReplyStep3.getLevel()), n).toByteArray();
|
|
||||||
|
|
||||||
System.arraycopy(
|
|
||||||
solution, Math.abs(solution.length - 64),
|
|
||||||
y, solution.length < 64 ? 64 - solution.length : 0,
|
|
||||||
solution.length < 64 ? solution.length : 64
|
|
||||||
);
|
|
||||||
|
|
||||||
// Build response
|
// Build response
|
||||||
PacketBody8Init1.Step4 step4 = new PacketBody8Init1.Step4();
|
PacketBody8Init1.Step4 step4 = new PacketBody8Init1.Step4();
|
||||||
@@ -360,4 +351,17 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
|
|||||||
|
|
||||||
getClient().writePacket(new PacketBody2Command(ProtocolRole.CLIENT, clientinit));
|
getClient().writePacket(new PacketBody2Command(ProtocolRole.CLIENT, clientinit));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The puzzle's answer as 64 unsigned big-endian bytes. {@link BigInteger#toByteArray()}
|
||||||
|
* adds a sign byte when the top bit is set and drops leading zero bytes, so it can be
|
||||||
|
* 65 bytes or fewer than 64.
|
||||||
|
*/
|
||||||
|
static byte[] unsigned64(BigInteger value) {
|
||||||
|
byte[] bytes = value.toByteArray();
|
||||||
|
byte[] y = new byte[64];
|
||||||
|
int length = Math.min(bytes.length, 64);
|
||||||
|
System.arraycopy(bytes, bytes.length - length, y, 64 - length, length);
|
||||||
|
return y;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
package com.github.manevolent.ts3j.protocol.packet.handler.client;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class PuzzleAnswerTest extends TestCase {
|
||||||
|
private static byte[] bytes(int length, int first) {
|
||||||
|
byte[] b = new byte[length];
|
||||||
|
Arrays.fill(b, (byte) 0x11);
|
||||||
|
b[0] = (byte) first;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Top bit set: toByteArray() gives 65 bytes, the first a sign byte. */
|
||||||
|
public void testDropsTheSignByte() {
|
||||||
|
byte[] answer = bytes(64, 0x80);
|
||||||
|
assertTrue(Arrays.equals(answer, LocalClientHandlerConnecting.unsigned64(new BigInteger(1, answer))));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testKeepsSixtyFourBytes() {
|
||||||
|
byte[] answer = bytes(64, 0x7f);
|
||||||
|
assertTrue(Arrays.equals(answer, LocalClientHandlerConnecting.unsigned64(new BigInteger(1, answer))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A leading zero byte: toByteArray() gives 63 bytes, which used to fail the connect. */
|
||||||
|
public void testPadsAShortAnswerWithZeros() {
|
||||||
|
byte[] answer = bytes(64, 0x00);
|
||||||
|
answer[1] = 0x42;
|
||||||
|
assertTrue(Arrays.equals(answer, LocalClientHandlerConnecting.unsigned64(new BigInteger(1, answer))));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user