100 lines
3.0 KiB
C#
100 lines
3.0 KiB
C#
using Sodium;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace XIVChatCommon {
|
|
public static class KeyExchange {
|
|
public static SessionKeys ClientSessionKeys(KeyPair client, byte[] serverPublic) {
|
|
var secret = ScalarMult.Mult(client.PrivateKey, serverPublic);
|
|
|
|
var combined = secret
|
|
.Concat(client.PublicKey)
|
|
.Concat(serverPublic)
|
|
.ToArray();
|
|
|
|
var hash = GenericHash.Hash(combined, null, 64);
|
|
|
|
byte[] rx = new byte[32];
|
|
byte[] tx = new byte[32];
|
|
|
|
for (var i = 0; i < 32; i++) {
|
|
rx[i] = hash[i];
|
|
tx[i] = hash[i + 32];
|
|
}
|
|
|
|
return new SessionKeys(rx, tx);
|
|
}
|
|
|
|
public static SessionKeys ServerSessionKeys(KeyPair server, byte[] clientPublic) {
|
|
var secret = ScalarMult.Mult(server.PrivateKey, clientPublic);
|
|
|
|
var combined = secret
|
|
.Concat(clientPublic)
|
|
.Concat(server.PublicKey)
|
|
.ToArray();
|
|
|
|
var hash = GenericHash.Hash(combined, null, 64);
|
|
|
|
byte[] rx = new byte[32];
|
|
byte[] tx = new byte[32];
|
|
|
|
for (var i = 0; i < 32; i++) {
|
|
tx[i] = hash[i];
|
|
rx[i] = hash[i + 32];
|
|
}
|
|
|
|
return new SessionKeys(rx, tx);
|
|
}
|
|
|
|
public static async Task<HandshakeInfo> ServerHandshake(KeyPair server, Stream stream) {
|
|
// get client public key
|
|
byte[] clientPublic = new byte[32];
|
|
await stream.ReadAsync(clientPublic, 0, clientPublic.Length);
|
|
|
|
// send our public key
|
|
await stream.WriteAsync(server.PublicKey, 0, server.PublicKey.Length);
|
|
await stream.FlushAsync();
|
|
|
|
// get shared secret and derive keys
|
|
var keys = ServerSessionKeys(server, clientPublic);
|
|
|
|
return new HandshakeInfo(clientPublic, keys);
|
|
}
|
|
|
|
public static async Task<HandshakeInfo> ClientHandshake(KeyPair client, Stream stream) {
|
|
// send our public key
|
|
await stream.WriteAsync(client.PublicKey, 0, client.PublicKey.Length);
|
|
|
|
// get server public key
|
|
byte[] serverPublic = new byte[32];
|
|
await stream.ReadAsync(serverPublic, 0, serverPublic.Length);
|
|
|
|
// get shared secret and derive keys
|
|
var keys = ClientSessionKeys(client, serverPublic);
|
|
|
|
return new HandshakeInfo(serverPublic, keys);
|
|
}
|
|
}
|
|
|
|
public class SessionKeys {
|
|
public readonly byte[] rx;
|
|
public readonly byte[] tx;
|
|
|
|
internal SessionKeys(byte[] rx, byte[] tx) {
|
|
this.rx = rx;
|
|
this.tx = tx;
|
|
}
|
|
}
|
|
|
|
public class HandshakeInfo {
|
|
public byte[] RemotePublicKey { get; }
|
|
public SessionKeys Keys { get; }
|
|
|
|
internal HandshakeInfo(byte[] remote, SessionKeys keys) {
|
|
this.RemotePublicKey = remote;
|
|
this.Keys = keys;
|
|
}
|
|
}
|
|
}
|