diff --git a/router/java/src/net/i2p/router/transport/udp/SSU2Payload.java b/router/java/src/net/i2p/router/transport/udp/SSU2Payload.java index f7e294df1..5948f82eb 100644 --- a/router/java/src/net/i2p/router/transport/udp/SSU2Payload.java +++ b/router/java/src/net/i2p/router/transport/udp/SSU2Payload.java @@ -112,9 +112,9 @@ class SSU2Payload { public void gotRelayIntro(Hash aliceHash, byte[] data); /** - * @param msg 1-7 + * @param msg 1-4 * @param status 0 = accept, 1-255 = reject - * @param h Alice or Charlie hash + * @param h Alice or Charlie hash for msg 2 and 4, null for msg 1 and 3 * @param data excludes flag, includes signature */ public void gotPeerTest(int msg, int status, Hash h, byte[] data); @@ -320,13 +320,25 @@ class SSU2Payload { case BLOCK_PEERTEST: { if (isHandshake) throw new IOException("Illegal block in handshake: " + type); - if (len < 92) // 32 byte hash + 20 byte data w/ IPv4 + 40 byte DSA sig + if (len < 20) // 20 byte data w/ IPv4 (hash and sig optional) throw new IOException("Bad length for PEERTEST: " + len); int mnum = payload[i] & 0xff; + if (mnum == 0 || mnum > 4) + throw new DataFormatException("Bad PEERTEST number: " + mnum); int resp = payload[i + 1] & 0xff; - Hash h = Hash.create(payload, i + 3); // skip flag - byte[] data = new byte[len - (3 + Hash.HASH_LENGTH)]; - System.arraycopy(payload, i + 3 + Hash.HASH_LENGTH, data, 0, data.length); + int o = i + 3; // skip flag + int datalen; + Hash h; + if (mnum == 2 || mnum == 4) { + h = Hash.create(payload, o); + datalen = len - (3 + Hash.HASH_LENGTH); + o += Hash.HASH_LENGTH; + } else { + datalen = len - 3; + h = null; + } + byte[] data = new byte[datalen]; + System.arraycopy(payload, o, data, 0, datalen); cb.gotPeerTest(mnum, resp, h, data); break; } diff --git a/router/java/src/net/i2p/router/transport/udp/SSU2Util.java b/router/java/src/net/i2p/router/transport/udp/SSU2Util.java index a5b565785..9988bb534 100644 --- a/router/java/src/net/i2p/router/transport/udp/SSU2Util.java +++ b/router/java/src/net/i2p/router/transport/udp/SSU2Util.java @@ -132,18 +132,61 @@ final class SSU2Util { return rv; } + /** + * Make the data for the peer test block + * + * @param h to be included in sig, not included in data + * @param h2 may be null, to be included in sig, not included in data + * @return null on failure + */ + public static byte[] createPeerTestData(I2PAppContext ctx, Hash h, Hash h2, + PeerTestState.Role role, long nonce, byte[] ip, int port, + SigningPrivateKey spk) { + int datalen = 13 + ip.length; + byte[] data = new byte[datalen + spk.getType().getSigLen()]; + if (role == PeerTestState.Role.BOB) + throw new IllegalArgumentException(); + data[0] = (byte) ((role == PeerTestState.Role.ALICE) ? 1 : 3); + data[1] = 2; // version + DataHelper.toLong(data, 2, 4, nonce); + DataHelper.toLong(data, 6, 4, ctx.clock().now() / 1000); + data[10] = (byte) ip.length; + System.arraycopy(ip, 0, data, 11, ip.length); + DataHelper.toLong(data, 11 + ip.length, 2, port); + Signature sig = sign(ctx, PEER_TEST_PROLOGUE, h, h2, data, datalen, spk); + if (sig == null) + return null; + byte[] s = sig.getData(); + System.arraycopy(s, 0, data, datalen, s.length); + return data; + } + /** * Sign the relay or peer test data, using * the prologue and hash as the initial data, * and then the provided data. * + * @param data if desired, leave room at end for sig + * @param datalen the length of the data to be signed + * @param h to be included in sig, not included in data + * @param h2 may be null, to be included in sig, not included in data * @return null on failure */ - public static Signature sign(I2PAppContext ctx, byte[] prologue, Hash h, byte[] data, SigningPrivateKey spk) { - byte[] buf = new byte[prologue.length + Hash.HASH_LENGTH + data.length]; + public static Signature sign(I2PAppContext ctx, byte[] prologue, Hash h, Hash h2, + byte[] data, int datalen, SigningPrivateKey spk) { + int len = prologue.length + Hash.HASH_LENGTH + datalen; + if (h2 != null) + len += Hash.HASH_LENGTH; + byte[] buf = new byte[len]; System.arraycopy(prologue, 0, buf, 0, prologue.length); System.arraycopy(h.getData(), 0, buf, prologue.length, Hash.HASH_LENGTH); - System.arraycopy(data, 0, buf, prologue.length + Hash.HASH_LENGTH, data.length); + System.arraycopy(h.getData(), 0, buf, prologue.length, Hash.HASH_LENGTH); + int off = prologue.length + Hash.HASH_LENGTH; + if (h2 != null) { + System.arraycopy(h2.getData(), 0, buf, off, Hash.HASH_LENGTH); + off += Hash.HASH_LENGTH; + } + System.arraycopy(data, 0, buf, off, datalen); return ctx.dsa().sign(buf, spk); } @@ -151,14 +194,24 @@ final class SSU2Util { * Validate the signed relay or peer test data, using * the prologue and hash as the initial data, * and then the provided data which ends with a signature of the specified type. + * + * @param h2 may be null */ - public static boolean validateSig(I2PAppContext ctx, byte[] prologue, Hash h, byte[] data, SigningPublicKey spk) { + public static boolean validateSig(I2PAppContext ctx, byte[] prologue, Hash h, Hash h2, byte[] data, SigningPublicKey spk) { SigType type = spk.getType(); int siglen = type.getSigLen(); - byte[] buf = new byte[prologue.length + Hash.HASH_LENGTH + data.length - siglen]; + int len = prologue.length + Hash.HASH_LENGTH + data.length - siglen; + if (h2 != null) + len += Hash.HASH_LENGTH; + byte[] buf = new byte[len]; System.arraycopy(prologue, 0, buf, 0, prologue.length); System.arraycopy(h.getData(), 0, buf, prologue.length, Hash.HASH_LENGTH); - System.arraycopy(data, 0, buf, prologue.length + Hash.HASH_LENGTH, data.length - siglen); + int off = prologue.length + Hash.HASH_LENGTH; + if (h2 != null) { + System.arraycopy(h2.getData(), 0, buf, off, Hash.HASH_LENGTH); + off += Hash.HASH_LENGTH; + } + System.arraycopy(data, 0, buf, off, data.length - siglen); byte[] bsig = new byte[siglen]; System.arraycopy(data, data.length - siglen, bsig, 0, siglen); Signature sig = new Signature(type, bsig);