Compare commits

...

2 Commits

Author SHA1 Message Date
b0bb4057f6
Add NetworkEncryptionCipherPacket, IInternalCache.
Fix-up EncryptedPacket.
Update CALMNETPacketFactory.
2023-05-23 12:59:07 +01:00
bde5860b47
Add the ability to set secret sending for NetworkEncryptionUpgradePackets on or off. 2023-05-22 13:30:45 +01:00
5 changed files with 367 additions and 11 deletions

View File

@ -0,0 +1,23 @@
package com.captainalm.lib.calmnet.packet;
/**
* This interface allows getting and setting if the
* internal cache should be used within a class instance.
*
* @author Captain ALM
*/
public interface IInternalCache {
/**
* Gets if the internal cache is used.
*
* @return If the internal cache is used.
*/
boolean isCacheUsed();
/**
* Sets if the internal cache is used.
*
* @param used If the internal cache is used.
*/
void setCacheUsed(boolean used);
}

View File

@ -23,7 +23,7 @@ import static com.captainalm.lib.calmnet.packet.PacketLoader.readByteFromInputSt
* *
* @author Captain ALM * @author Captain ALM
*/ */
public class EncryptedPacket implements IStreamedPacket { public class EncryptedPacket implements IStreamedPacket, IInternalCache {
/* /*
* Packet Format: * Packet Format:
* *
@ -356,10 +356,7 @@ public class EncryptedPacket implements IStreamedPacket {
int flag = readByteFromInputStream(inputStream) & 0xff; int flag = readByteFromInputStream(inputStream) & 0xff;
if (size < 5) throw new IOException("inputStream end of stream"); if (size < 5) throw new IOException("inputStream end of stream");
int cipherLenCache = (readByteFromInputStream(inputStream) & 0xff) * 16777216; int cipherLenCache = PacketLoader.readInteger(inputStream);
cipherLenCache += (readByteFromInputStream(inputStream) & 0xff) * 65536;
cipherLenCache += (readByteFromInputStream(inputStream) & 0xff) * 256;
cipherLenCache += (readByteFromInputStream(inputStream) & 0xff);
if (cipherLenCache < 1) throw new PacketException("cipher length less than 1"); if (cipherLenCache < 1) throw new PacketException("cipher length less than 1");
if (size < 5 + cipherLenCache) throw new IOException("inputStream end of stream"); if (size < 5 + cipherLenCache) throw new IOException("inputStream end of stream");
@ -378,10 +375,7 @@ public class EncryptedPacket implements IStreamedPacket {
trailingArrayLengthCache = 0; trailingArrayLengthCache = 0;
if ((flag & 1) == 1) { if ((flag & 1) == 1) {
if (size < 9 + cipherLenCache) throw new IOException("inputStream end of stream"); if (size < 9 + cipherLenCache) throw new IOException("inputStream end of stream");
trailingArrayLengthCache = (readByteFromInputStream(inputStream) & 0xff) * 16777216; trailingArrayLengthCache = PacketLoader.readByteFromInputStream(inputStream);
trailingArrayLengthCache += (readByteFromInputStream(inputStream) & 0xff) * 65536;
trailingArrayLengthCache += (readByteFromInputStream(inputStream) & 0xff) * 256;
trailingArrayLengthCache += (readByteFromInputStream(inputStream) & 0xff);
if (trailingArrayLengthCache < 1) throw new PacketException("trailer length less than 1"); if (trailingArrayLengthCache < 1) throw new PacketException("trailer length less than 1");
} }
@ -538,6 +532,7 @@ public class EncryptedPacket implements IStreamedPacket {
* *
* @return If the encrypted data is cached. * @return If the encrypted data is cached.
*/ */
@Override
public boolean isCacheUsed() { public boolean isCacheUsed() {
return useCache; return useCache;
} }
@ -547,6 +542,7 @@ public class EncryptedPacket implements IStreamedPacket {
* *
* @param used If the encrypted data should be cached. * @param used If the encrypted data should be cached.
*/ */
@Override
public void setCacheUsed(boolean used) { public void setCacheUsed(boolean used) {
synchronized (slock) { synchronized (slock) {
useCache = used; useCache = used;

View File

@ -0,0 +1,315 @@
package com.captainalm.lib.calmnet.packet.core;
import com.captainalm.lib.calmnet.packet.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
/**
* This class provides the ability for supporting streams to negotiate a cipher.
* <p>
* Major ID: 255
* Minor ID: 250
* </p>
*
* @author Captain ALM
*/
public class NetworkEncryptionCipherPacket implements IStreamedPacket, IAcknowledgement, IInternalCache {
private static final PacketProtocolInformation protocol = new PacketProtocolInformation((byte) 255, (byte) 250);
protected Boolean acknowledgement;
protected String[] ciphers;
protected byte[] cipherData;
protected boolean useCache;
protected final Object slock = new Object();
/**
* Constructs a new instance of NetworkEncryptionCipherPacket with the specified acknowledgement value and the specified ciphers.
*
* @param acknowledgement The acknowledgement value to use (Can be null).
* @param cipherNames The cipher names.
* @throws NullPointerException cipherNames is null.
*/
public NetworkEncryptionCipherPacket(Boolean acknowledgement, String[] cipherNames) {
if (cipherNames == null) throw new NullPointerException("cipherNames is null");
this.acknowledgement = acknowledgement;
this.ciphers = cipherNames;
}
/**
* Gets if the class instance is an Acknowledgement.
*
* @return If the class instance is an Acknowledgement.
*/
@Override
public boolean isAcknowledgement() {
return (acknowledgement != null && acknowledgement);
}
/**
* Gets if the packet is valid.
*
* @return Is the packet valid?
*/
@Override
public boolean isValid() {
return (acknowledgement != null && ciphers != null);
}
/**
* Gets the protocol information.
*
* @return The protocol information.
*/
@Override
public PacketProtocolInformation getProtocol() {
return protocol;
}
/**
* Gets the protocol information statically.
*
* @return The protocol information.
*/
public static PacketProtocolInformation getTheProtocol() {
return protocol;
}
protected void processCache() throws PacketException {
if (cipherData == null) {
if (acknowledgement == null || ciphers == null) throw new PacketException("no data");
ByteArrayOutputStream arrayData = new ByteArrayOutputStream();
arrayData.write((acknowledgement) ? (byte) 1 : (byte) 0);
try {
PacketLoader.writeInteger(arrayData, ciphers.length);
} catch (IOException e) {
throw new PacketException(e);
}
for (String c : ciphers) {
if (c == null) throw new PacketException("no data in entry");
try {
if (c.length() < 1) {
PacketLoader.writeInteger(arrayData, 0);
} else {
byte[] d = c.getBytes(StandardCharsets.UTF_8);
PacketLoader.writeInteger(arrayData, d.length);
arrayData.write(d);
}
} catch (IOException e) {
throw new PacketException(e);
}
}
cipherData = arrayData.toByteArray();
}
}
/**
* Saves the packet payload to a byte array.
*
* @return The packet payload data.
* @throws PacketException An Exception has occurred.
*/
@Override
public byte[] savePayload() throws PacketException {
synchronized (slock) {
processCache();
if (useCache) return cipherData; else {
byte[] toret = cipherData;
cipherData = null;
return toret;
}
}
}
/**
* Loads the packet payload from save data.
*
* @param packetData The packet payload data.
* @throws NullPointerException The new store data is null.
* @throws PacketException An Exception has occurred.
*/
@Override
public void loadPayload(byte[] packetData) throws PacketException {
if (packetData == null) throw new NullPointerException("packetData is null");
if (packetData.length < 5) throw new PacketException("no data");
synchronized (slock) {
acknowledgement = (packetData[0] == 1);
if (!acknowledgement && packetData[0] != 0) acknowledgement = null;
int index = 1;
int recordCount = (packetData[index++] & 0xff) * 16777216;
recordCount += (packetData[index++] & 0xff) * 65536;
recordCount += (packetData[index++] & 0xff) * 256;
recordCount += (packetData[index++] & 0xff);
if (recordCount < 0) throw new PacketException("record count less than 0");
if (useCache) cipherData = packetData;
ciphers = new String[recordCount];
for (int i = 0; i < recordCount; i++) {
int recordLength = (packetData[index++] & 0xff) * 16777216;
recordLength += (packetData[index++] & 0xff) * 65536;
recordLength += (packetData[index++] & 0xff) * 256;
recordLength += (packetData[index++] & 0xff);
if (recordLength < 0) throw new PacketException("record length less than 0");
byte[] currentRecord = new byte[recordLength];
if (recordLength > 0) {
System.arraycopy(packetData, index, currentRecord, 0, recordLength);
index += recordLength;
ciphers[i] = new String(currentRecord, StandardCharsets.UTF_8);
} else {
ciphers[i] = "";
}
}
}
}
/**
* Reads payload data to an {@link OutputStream}.
*
* @param outputStream The output stream to read data to.
* @throws NullPointerException outputStream is null.
* @throws IOException An IO Exception has occurred.
* @throws PacketException An Exception has occurred.
*/
@Override
public void readData(OutputStream outputStream) throws IOException, PacketException {
if (outputStream == null) throw new NullPointerException("outputStream is null");
synchronized (slock) {
if (useCache) {
processCache();
outputStream.write(cipherData);
} else {
outputStream.write((acknowledgement) ? (byte) 1 : (byte) 0);
try {
PacketLoader.writeInteger(outputStream, ciphers.length);
} catch (IOException e) {
throw new PacketException(e);
}
for (String c : ciphers) {
if (c == null) throw new PacketException("no data in entry");
try {
if (c.length() < 1) {
PacketLoader.writeInteger(outputStream, 0);
} else {
byte[] d = c.getBytes(StandardCharsets.UTF_8);
PacketLoader.writeInteger(outputStream, d.length);
outputStream.write(d);
}
} catch (IOException e) {
throw new PacketException(e);
}
}
}
}
}
/**
* Writes payload data from an {@link InputStream}.
*
* @param inputStream The input stream to write data from.
* @param size The size of the input payload in bytes.
* @throws NullPointerException inputStream is null.
* @throws IllegalArgumentException size is less than 0.
* @throws IOException An IO Exception has occurred.
* @throws PacketException An Exception has occurred.
*/
@Override
public void writeData(InputStream inputStream, int size) throws IOException, PacketException {
if (inputStream == null) throw new NullPointerException("inputStream is null");
if (size < 0) throw new IllegalArgumentException("size is less than 0");
synchronized (slock) {
if (size < 1) throw new IOException("inputStream end of stream");
byte aknByte = PacketLoader.readByteFromInputStream(inputStream);
acknowledgement = (aknByte == 1);
if (!acknowledgement && aknByte != 0) acknowledgement = null;
if (size < 5) throw new IOException("inputStream end of stream");
int recordCount = PacketLoader.readInteger(inputStream);
if (recordCount < 0) throw new PacketException("record count less than 0");
cipherData = null;
ciphers = new String[recordCount];
for (int i = 0; i < recordCount; i++) {
int recordLength = PacketLoader.readInteger(inputStream);
if (recordLength < 0) throw new PacketException("record length less than 0");
if (recordLength > 0) {
byte[] currentRecord = PacketLoader.readArrayFromInputStream(inputStream, recordLength);
ciphers[i] = new String(currentRecord, StandardCharsets.UTF_8);
} else {
ciphers[i] = "";
}
}
}
}
/**
* Gets the size of the output data.
*
* @return The size of the output data in bytes.
* @throws PacketException An Exception has occurred.
*/
@Override
public int getSize() throws PacketException {
synchronized (slock) {
processCache();
if (useCache) return cipherData.length; else {
int toret = cipherData.length;
cipherData = null;
return toret;
}
}
}
/**
* Gets the cipher names this packet contains.
*
* @return An array of cipher names.
*/
public String[] getCiphers() {
return ciphers;
}
/**
* Sets the cipher names this packet contains.
*
* @param cipherNames The array of cipher names.
* @throws NullPointerException cipherNames is null.
*/
public void setCiphers(String[] cipherNames) {
if (cipherNames == null) throw new NullPointerException("cipherNames is null");
synchronized (slock) {
ciphers = cipherNames;
cipherData = null;
}
}
/**
* Gets if the cipher information is cached.
*
* @return If the cipher information is cached.
*/
@Override
public boolean isCacheUsed() {
return useCache;
}
/**
* Sets if the cipher information is cached.
*
* @param used If the cipher information is cached.
*/
@Override
public void setCacheUsed(boolean used) {
synchronized (slock) {
useCache = used;
if (!useCache)
cipherData = null;
}
}
}

View File

@ -24,6 +24,7 @@ public class NetworkEncryptionUpgradePacket implements IPacket, IAcknowledgement
protected boolean upgrade; protected boolean upgrade;
protected boolean base64ed; protected boolean base64ed;
protected ICipherFactory cipherFactory; protected ICipherFactory cipherFactory;
protected boolean sendSecrets;
protected final Object slock = new Object(); protected final Object slock = new Object();
@ -82,7 +83,7 @@ public class NetworkEncryptionUpgradePacket implements IPacket, IAcknowledgement
synchronized (slock) { synchronized (slock) {
if (acknowledgement == null) throw new PacketException("no data"); if (acknowledgement == null) throw new PacketException("no data");
byte[] cipherBytes = (cipherFactory == null) ? null : cipherFactory.getSettings(); byte[] cipherBytes = (cipherFactory == null) ? null : (sendSecrets) ? cipherFactory.getSettings() : cipherFactory.getSettingsNoSecrets();
byte[] toret = new byte[2 + ((cipherBytes == null) ? 0 : cipherBytes.length)]; byte[] toret = new byte[2 + ((cipherBytes == null) ? 0 : cipherBytes.length)];
toret[0] = (acknowledgement) ? (byte) 1 : (byte) 0; toret[0] = (acknowledgement) ? (byte) 1 : (byte) 0;
toret[1] = (byte) (((upgrade) ? 1 : 0) + ((base64ed) ? 2 : 0)); toret[1] = (byte) (((upgrade) ? 1 : 0) + ((base64ed) ? 2 : 0));
@ -196,4 +197,24 @@ public class NetworkEncryptionUpgradePacket implements IPacket, IAcknowledgement
public boolean isAcknowledgement() { public boolean isAcknowledgement() {
return (acknowledgement != null && acknowledgement); return (acknowledgement != null && acknowledgement);
} }
/**
* Gets if secrets are sent as part of cipher settings.
*
* @return If the secrets are part of the cipher settings.
*/
public boolean areSecretsSent() {
return sendSecrets;
}
/**
* Sets if secrets should be sent as part of cipher settings.
*
* @param sendSecrets If secrets are part of the cipher settings.
*/
public void setIfSecretsSent(boolean sendSecrets) {
synchronized (slock) {
this.sendSecrets = sendSecrets;
}
}
} }

View File

@ -55,6 +55,7 @@ public class CALMNETPacketFactory implements IPacketFactory {
public IPacket getPacket(PacketProtocolInformation information) { public IPacket getPacket(PacketProtocolInformation information) {
if (information == null) throw new NullPointerException("information is null"); if (information == null) throw new NullPointerException("information is null");
if (information.equals(NetworkEncryptionCipherPacket.getTheProtocol())) return new NetworkEncryptionCipherPacket(null, new String[0]);
if (information.equals(Base64Packet.getTheProtocol())) return new Base64Packet(factoryToUse, loaderToUse); if (information.equals(Base64Packet.getTheProtocol())) return new Base64Packet(factoryToUse, loaderToUse);
if (information.equals(EncryptedPacket.getTheProtocol()) && cipherToUse != null) return new EncryptedPacket(factoryToUse, loaderToUse, cipherToUse); if (information.equals(EncryptedPacket.getTheProtocol()) && cipherToUse != null) return new EncryptedPacket(factoryToUse, loaderToUse, cipherToUse);
if (information.equals(NetworkEncryptionUpgradePacket.getTheProtocol())) return new NetworkEncryptionUpgradePacket(null, false, false, cipherToUse); if (information.equals(NetworkEncryptionUpgradePacket.getTheProtocol())) return new NetworkEncryptionUpgradePacket(null, false, false, cipherToUse);