Implemented counting AES-GCM IV
This commit is contained in:
parent
dda6086a2c
commit
a82dbf81b5
@ -49,6 +49,7 @@ class MetaChannel {
|
|||||||
|
|
||||||
public AsymmetricCipherKeyPair ecdhKey; // used for ECC Diffie-Hellman-Merkle key exchanges: see http://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange
|
public AsymmetricCipherKeyPair ecdhKey; // used for ECC Diffie-Hellman-Merkle key exchanges: see http://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange
|
||||||
|
|
||||||
|
// since we are using AES-GCM, the aesIV here **MUST** be exactly 12 bytes
|
||||||
public byte[] aesKey;
|
public byte[] aesKey;
|
||||||
public byte[] aesIV;
|
public byte[] aesIV;
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package dorkbox.network.connection.registration.remote;
|
package dorkbox.network.connection.registration.remote;
|
||||||
|
|
||||||
|
import com.esotericsoftware.kryo.io.Input;
|
||||||
import dorkbox.network.connection.Connection;
|
import dorkbox.network.connection.Connection;
|
||||||
import dorkbox.network.connection.ConnectionImpl;
|
import dorkbox.network.connection.ConnectionImpl;
|
||||||
import dorkbox.network.connection.EndPoint;
|
import dorkbox.network.connection.EndPoint;
|
||||||
@ -28,6 +29,7 @@ import dorkbox.network.pipeline.udp.KryoEncoderUdpCrypto;
|
|||||||
import dorkbox.network.util.CryptoSerializationManager;
|
import dorkbox.network.util.CryptoSerializationManager;
|
||||||
import dorkbox.util.collections.IntMap;
|
import dorkbox.util.collections.IntMap;
|
||||||
import dorkbox.util.collections.IntMap.Entries;
|
import dorkbox.util.collections.IntMap.Entries;
|
||||||
|
import dorkbox.util.serialization.EccPublicKeySerializer;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
@ -37,8 +39,11 @@ import io.netty.channel.socket.nio.NioDatagramChannel;
|
|||||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||||
import io.netty.channel.udt.nio.NioUdtByteConnectorChannel;
|
import io.netty.channel.udt.nio.NioUdtByteConnectorChannel;
|
||||||
import io.netty.handler.timeout.IdleStateHandler;
|
import io.netty.handler.timeout.IdleStateHandler;
|
||||||
|
import io.netty.util.ReferenceCountUtil;
|
||||||
import org.bouncycastle.crypto.engines.AESFastEngine;
|
import org.bouncycastle.crypto.engines.AESFastEngine;
|
||||||
import org.bouncycastle.crypto.modes.GCMBlockCipher;
|
import org.bouncycastle.crypto.modes.GCMBlockCipher;
|
||||||
|
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
@ -295,6 +300,59 @@ class RegistrationRemoteHandler<C extends Connection> extends RegistrationHandle
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected final
|
||||||
|
ECPublicKeyParameters verifyPayload(final Object message,
|
||||||
|
final Channel channel,
|
||||||
|
final RegistrationWrapper registrationWrapper,
|
||||||
|
final Logger logger,
|
||||||
|
final byte[] payload) {
|
||||||
|
|
||||||
|
if (payload.length == 0) {
|
||||||
|
logger.error("Invalid decryption of payload. Aborting.");
|
||||||
|
shutdown(registrationWrapper, channel);
|
||||||
|
|
||||||
|
ReferenceCountUtil.release(message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ECPublicKeyParameters ecdhPubKey = EccPublicKeySerializer.read(new Input(payload));
|
||||||
|
|
||||||
|
if (ecdhPubKey == null) {
|
||||||
|
logger.error("Invalid decode of ecdh public key. Aborting.");
|
||||||
|
shutdown(registrationWrapper, channel);
|
||||||
|
|
||||||
|
ReferenceCountUtil.release(message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ecdhPubKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final
|
||||||
|
boolean verifyAesInfo(final Object message,
|
||||||
|
final Channel channel,
|
||||||
|
final RegistrationWrapper registrationWrapper,
|
||||||
|
final MetaChannel metaChannel,
|
||||||
|
final Logger logger) {
|
||||||
|
|
||||||
|
if (metaChannel.aesKey.length != 32) {
|
||||||
|
logger.error("Fatal error trying to use AES key (wrong key length).");
|
||||||
|
shutdown(registrationWrapper, channel);
|
||||||
|
|
||||||
|
ReferenceCountUtil.release(message);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// IV length must == 12 because we are using GCM!
|
||||||
|
else if (metaChannel.aesIV.length != 12) {
|
||||||
|
logger.error("Fatal error trying to use AES IV (wrong IV length).");
|
||||||
|
shutdown(registrationWrapper, channel);
|
||||||
|
|
||||||
|
ReferenceCountUtil.release(message);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// have to setup AFTER establish connection, data, as we don't want to enable AES until we're ready.
|
// have to setup AFTER establish connection, data, as we don't want to enable AES until we're ready.
|
||||||
@SuppressWarnings("AutoUnboxing")
|
@SuppressWarnings("AutoUnboxing")
|
||||||
protected final
|
protected final
|
||||||
|
@ -160,7 +160,7 @@ class RegistrationRemoteHandlerClientTCP<C extends Connection> extends Registrat
|
|||||||
channel.writeAndFlush(registration);
|
channel.writeAndFlush(registration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({"AutoUnboxing", "AutoBoxing"})
|
@SuppressWarnings({"AutoUnboxing", "AutoBoxing", "Duplicates"})
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void channelRead(final ChannelHandlerContext context, final Object message) throws Exception {
|
void channelRead(final ChannelHandlerContext context, final Object message) throws Exception {
|
||||||
@ -252,7 +252,6 @@ class RegistrationRemoteHandlerClientTCP<C extends Connection> extends Registrat
|
|||||||
*/
|
*/
|
||||||
byte[] ecdhPubKeyBytes = Arrays.copyOfRange(payload, intLength, payload.length);
|
byte[] ecdhPubKeyBytes = Arrays.copyOfRange(payload, intLength, payload.length);
|
||||||
ECPublicKeyParameters ecdhPubKey = EccPublicKeySerializer.read(new Input(ecdhPubKeyBytes));
|
ECPublicKeyParameters ecdhPubKey = EccPublicKeySerializer.read(new Input(ecdhPubKeyBytes));
|
||||||
|
|
||||||
if (ecdhPubKey == null) {
|
if (ecdhPubKey == null) {
|
||||||
logger2.error("Invalid decode of ecdh public key. Aborting.");
|
logger2.error("Invalid decode of ecdh public key. Aborting.");
|
||||||
shutdown(registrationWrapper2, channel);
|
shutdown(registrationWrapper2, channel);
|
||||||
@ -290,18 +289,13 @@ class RegistrationRemoteHandlerClientTCP<C extends Connection> extends Registrat
|
|||||||
sha384.doFinal(digest, 0);
|
sha384.doFinal(digest, 0);
|
||||||
|
|
||||||
metaChannel.aesKey = Arrays.copyOfRange(digest, 0, 32); // 256bit keysize (32 bytes)
|
metaChannel.aesKey = Arrays.copyOfRange(digest, 0, 32); // 256bit keysize (32 bytes)
|
||||||
metaChannel.aesIV = Arrays.copyOfRange(digest, 32, 48); // 128bit blocksize (16 bytes)
|
metaChannel.aesIV = Arrays.copyOfRange(digest, 32, 44); // 96bit blocksize (12 bytes) required by AES-GCM
|
||||||
|
|
||||||
// abort if something messed up!
|
// abort if something messed up!
|
||||||
if (metaChannel.aesKey.length != 32) {
|
if (verifyAesInfo(message, channel, registrationWrapper2, metaChannel, logger2)) {
|
||||||
logger2.error("Fatal error trying to use AES key (wrong key length).");
|
|
||||||
shutdown(registrationWrapper2, channel);
|
|
||||||
|
|
||||||
ReferenceCountUtil.release(message);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Registration register = new Registration();
|
Registration register = new Registration();
|
||||||
|
|
||||||
// encrypt the ECDH public key using our previous AES info
|
// encrypt the ECDH public key using our previous AES info
|
||||||
|
@ -55,7 +55,7 @@ class RegistrationRemoteHandlerServerTCP<C extends Connection> extends Registrat
|
|||||||
private static final ECParameterSpec eccSpec = ECNamedCurveTable.getParameterSpec(CryptoECC.p521_curve);
|
private static final ECParameterSpec eccSpec = ECNamedCurveTable.getParameterSpec(CryptoECC.p521_curve);
|
||||||
private final Object ecdhKeyLock = new Object();
|
private final Object ecdhKeyLock = new Object();
|
||||||
private final ThreadLocal<IESEngine> eccEngineLocal = new ThreadLocal<IESEngine>();
|
private final ThreadLocal<IESEngine> eccEngineLocal = new ThreadLocal<IESEngine>();
|
||||||
private AsymmetricCipherKeyPair ecdhKeyPair = CryptoECC.generateKeyPair(eccSpec, new SecureRandom());
|
private AsymmetricCipherKeyPair ecdhKeyPair;
|
||||||
private volatile long ecdhTimeout = System.nanoTime();
|
private volatile long ecdhTimeout = System.nanoTime();
|
||||||
|
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ class RegistrationRemoteHandlerServerTCP<C extends Connection> extends Registrat
|
|||||||
*/
|
*/
|
||||||
private
|
private
|
||||||
AsymmetricCipherKeyPair getEchdKeyOnRotate(final SecureRandom secureRandom) {
|
AsymmetricCipherKeyPair getEchdKeyOnRotate(final SecureRandom secureRandom) {
|
||||||
if (System.nanoTime() - this.ecdhTimeout > ECDH_TIMEOUT) {
|
if (this.ecdhKeyPair == null || System.nanoTime() - this.ecdhTimeout > ECDH_TIMEOUT) {
|
||||||
synchronized (this.ecdhKeyLock) {
|
synchronized (this.ecdhKeyLock) {
|
||||||
this.ecdhTimeout = System.nanoTime();
|
this.ecdhTimeout = System.nanoTime();
|
||||||
this.ecdhKeyPair = CryptoECC.generateKeyPair(eccSpec, secureRandom);
|
this.ecdhKeyPair = CryptoECC.generateKeyPair(eccSpec, secureRandom);
|
||||||
@ -132,6 +132,7 @@ class RegistrationRemoteHandlerServerTCP<C extends Connection> extends Registrat
|
|||||||
/**
|
/**
|
||||||
* STEP 3-XXXXX: We pass registration messages around until we the registration handshake is complete!
|
* STEP 3-XXXXX: We pass registration messages around until we the registration handshake is complete!
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("Duplicates")
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void channelRead(ChannelHandlerContext context, Object message) throws Exception {
|
void channelRead(ChannelHandlerContext context, Object message) throws Exception {
|
||||||
@ -232,7 +233,7 @@ class RegistrationRemoteHandlerServerTCP<C extends Connection> extends Registrat
|
|||||||
|
|
||||||
// now we have to setup the TEMP AES key!
|
// now we have to setup the TEMP AES key!
|
||||||
metaChannel.aesKey = new byte[32]; // 256bit keysize (32 bytes)
|
metaChannel.aesKey = new byte[32]; // 256bit keysize (32 bytes)
|
||||||
metaChannel.aesIV = new byte[16]; // 128bit blocksize (16 bytes)
|
metaChannel.aesIV = new byte[12]; // 96bit blocksize (12 bytes) required by AES-GCM
|
||||||
secureRandom.nextBytes(metaChannel.aesKey);
|
secureRandom.nextBytes(metaChannel.aesKey);
|
||||||
secureRandom.nextBytes(metaChannel.aesIV);
|
secureRandom.nextBytes(metaChannel.aesIV);
|
||||||
|
|
||||||
@ -282,6 +283,7 @@ class RegistrationRemoteHandlerServerTCP<C extends Connection> extends Registrat
|
|||||||
registration.payload,
|
registration.payload,
|
||||||
logger);
|
logger);
|
||||||
|
|
||||||
|
// abort if we cannot properly get the key info from the payload
|
||||||
if (payload.length == 0) {
|
if (payload.length == 0) {
|
||||||
logger2.error("Invalid decryption of payload. Aborting.");
|
logger2.error("Invalid decryption of payload. Aborting.");
|
||||||
shutdown(registrationWrapper2, channel);
|
shutdown(registrationWrapper2, channel);
|
||||||
@ -290,8 +292,11 @@ class RegistrationRemoteHandlerServerTCP<C extends Connection> extends Registrat
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Diffie-Hellman-Merkle key exchange for the AES key
|
||||||
|
* see http://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange
|
||||||
|
*/
|
||||||
ECPublicKeyParameters ecdhPubKey = EccPublicKeySerializer.read(new Input(payload));
|
ECPublicKeyParameters ecdhPubKey = EccPublicKeySerializer.read(new Input(payload));
|
||||||
|
|
||||||
if (ecdhPubKey == null) {
|
if (ecdhPubKey == null) {
|
||||||
logger2.error("Invalid decode of ecdh public key. Aborting.");
|
logger2.error("Invalid decode of ecdh public key. Aborting.");
|
||||||
shutdown(registrationWrapper2, channel);
|
shutdown(registrationWrapper2, channel);
|
||||||
@ -319,7 +324,12 @@ class RegistrationRemoteHandlerServerTCP<C extends Connection> extends Registrat
|
|||||||
sha384.doFinal(digest, 0);
|
sha384.doFinal(digest, 0);
|
||||||
|
|
||||||
metaChannel.aesKey = Arrays.copyOfRange(digest, 0, 32); // 256bit keysize (32 bytes)
|
metaChannel.aesKey = Arrays.copyOfRange(digest, 0, 32); // 256bit keysize (32 bytes)
|
||||||
metaChannel.aesIV = Arrays.copyOfRange(digest, 32, 48); // 128bit blocksize (16 bytes)
|
metaChannel.aesIV = Arrays.copyOfRange(digest, 32, 44); // 96bit blocksize (12 bytes) required by AES-GCM
|
||||||
|
|
||||||
|
// abort if something messed up!
|
||||||
|
if (verifyAesInfo(message, channel, registrationWrapper2, metaChannel, logger2)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// tell the client to continue it's registration process.
|
// tell the client to continue it's registration process.
|
||||||
channel.writeAndFlush(new Registration());
|
channel.writeAndFlush(new Registration());
|
||||||
@ -330,7 +340,7 @@ class RegistrationRemoteHandlerServerTCP<C extends Connection> extends Registrat
|
|||||||
channel.writeAndFlush(registration); // causes client to setup network connection & AES
|
channel.writeAndFlush(registration); // causes client to setup network connection & AES
|
||||||
|
|
||||||
setupConnectionCrypto(metaChannel);
|
setupConnectionCrypto(metaChannel);
|
||||||
// AES ENCRPYTION NOW USED
|
// AES ENCRYPTION NOW USED
|
||||||
|
|
||||||
// this sets up the pipeline for the server, so all the necessary handlers are ready to go
|
// this sets up the pipeline for the server, so all the necessary handlers are ready to go
|
||||||
establishConnection(metaChannel);
|
establishConnection(metaChannel);
|
||||||
|
Loading…
Reference in New Issue
Block a user