Implemented counting AES-GCM IV

This commit is contained in:
nathan 2016-03-09 03:04:10 +01:00
parent dda6086a2c
commit a82dbf81b5
4 changed files with 83 additions and 20 deletions

View File

@ -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
// since we are using AES-GCM, the aesIV here **MUST** be exactly 12 bytes
public byte[] aesKey;
public byte[] aesIV;

View File

@ -15,6 +15,7 @@
*/
package dorkbox.network.connection.registration.remote;
import com.esotericsoftware.kryo.io.Input;
import dorkbox.network.connection.Connection;
import dorkbox.network.connection.ConnectionImpl;
import dorkbox.network.connection.EndPoint;
@ -28,6 +29,7 @@ import dorkbox.network.pipeline.udp.KryoEncoderUdpCrypto;
import dorkbox.network.util.CryptoSerializationManager;
import dorkbox.util.collections.IntMap;
import dorkbox.util.collections.IntMap.Entries;
import dorkbox.util.serialization.EccPublicKeySerializer;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
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.udt.nio.NioUdtByteConnectorChannel;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.ReferenceCountUtil;
import org.bouncycastle.crypto.engines.AESFastEngine;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.slf4j.Logger;
import java.net.InetAddress;
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.
@SuppressWarnings("AutoUnboxing")
protected final

View File

@ -160,7 +160,7 @@ class RegistrationRemoteHandlerClientTCP<C extends Connection> extends Registrat
channel.writeAndFlush(registration);
}
@SuppressWarnings({"AutoUnboxing", "AutoBoxing"})
@SuppressWarnings({"AutoUnboxing", "AutoBoxing", "Duplicates"})
@Override
public
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);
ECPublicKeyParameters ecdhPubKey = EccPublicKeySerializer.read(new Input(ecdhPubKeyBytes));
if (ecdhPubKey == null) {
logger2.error("Invalid decode of ecdh public key. Aborting.");
shutdown(registrationWrapper2, channel);
@ -290,18 +289,13 @@ class RegistrationRemoteHandlerClientTCP<C extends Connection> extends Registrat
sha384.doFinal(digest, 0);
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 (metaChannel.aesKey.length != 32) {
logger2.error("Fatal error trying to use AES key (wrong key length).");
shutdown(registrationWrapper2, channel);
ReferenceCountUtil.release(message);
if (verifyAesInfo(message, channel, registrationWrapper2, metaChannel, logger2)) {
return;
}
Registration register = new Registration();
// encrypt the ECDH public key using our previous AES info

View File

@ -55,7 +55,7 @@ class RegistrationRemoteHandlerServerTCP<C extends Connection> extends Registrat
private static final ECParameterSpec eccSpec = ECNamedCurveTable.getParameterSpec(CryptoECC.p521_curve);
private final Object ecdhKeyLock = new Object();
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();
@ -81,7 +81,7 @@ class RegistrationRemoteHandlerServerTCP<C extends Connection> extends Registrat
*/
private
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) {
this.ecdhTimeout = System.nanoTime();
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!
*/
@SuppressWarnings("Duplicates")
@Override
public
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!
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.aesIV);
@ -245,11 +246,11 @@ class RegistrationRemoteHandlerServerTCP<C extends Connection> extends Registrat
register.eccParameters = CryptoECC.generateSharedParameters(secureRandom);
register.aesIV = metaChannel.aesIV;
register.aesKey = CryptoECC.encrypt(encrypt,
registrationWrapper2.getPrivateKey(),
metaChannel.publicKey,
register.eccParameters,
metaChannel.aesKey,
logger);
registrationWrapper2.getPrivateKey(),
metaChannel.publicKey,
register.eccParameters,
metaChannel.aesKey,
logger);
// now encrypt payload via AES
@ -282,6 +283,7 @@ class RegistrationRemoteHandlerServerTCP<C extends Connection> extends Registrat
registration.payload,
logger);
// abort if we cannot properly get the key info from the payload
if (payload.length == 0) {
logger2.error("Invalid decryption of payload. Aborting.");
shutdown(registrationWrapper2, channel);
@ -290,8 +292,11 @@ class RegistrationRemoteHandlerServerTCP<C extends Connection> extends Registrat
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));
if (ecdhPubKey == null) {
logger2.error("Invalid decode of ecdh public key. Aborting.");
shutdown(registrationWrapper2, channel);
@ -319,7 +324,12 @@ class RegistrationRemoteHandlerServerTCP<C extends Connection> extends Registrat
sha384.doFinal(digest, 0);
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.
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
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
establishConnection(metaChannel);