59 lines
1.9 KiB
C#
59 lines
1.9 KiB
C#
using System.Security.Cryptography;
|
|
using Org.BouncyCastle.Crypto.Parameters;
|
|
|
|
namespace VoiceCat.Crypto;
|
|
|
|
public sealed class ServerIdentity : IDisposable
|
|
{
|
|
private readonly byte[] seed;
|
|
private readonly byte[] publicKey;
|
|
private bool disposed;
|
|
|
|
private ServerIdentity(byte[] seed)
|
|
{
|
|
this.seed = seed;
|
|
publicKey = new Ed25519PrivateKeyParameters(seed, 0).GeneratePublicKey().GetEncoded();
|
|
}
|
|
|
|
public byte[] PublicKey => (byte[])publicKey.Clone();
|
|
public string Fingerprint => Convert.ToHexString(SHA256.HashData(publicKey));
|
|
|
|
public static ServerIdentity Generate() => new(RandomNumberGenerator.GetBytes(32));
|
|
|
|
public static ServerIdentity Load(string path)
|
|
{
|
|
byte[] data = File.ReadAllBytes(path);
|
|
try
|
|
{
|
|
if (data.Length != 96) throw new InvalidDataException("Server identity must contain 96 bytes.");
|
|
var identity = new ServerIdentity(data.AsSpan(32, 32).ToArray());
|
|
if (!CryptographicOperations.FixedTimeEquals(identity.publicKey, data.AsSpan(0, 32)) ||
|
|
!CryptographicOperations.FixedTimeEquals(identity.publicKey, data.AsSpan(64, 32)))
|
|
{
|
|
identity.Dispose();
|
|
throw new InvalidDataException("Server identity public key does not match its seed.");
|
|
}
|
|
return identity;
|
|
}
|
|
finally { CryptographicOperations.ZeroMemory(data); }
|
|
}
|
|
|
|
public void Save(string path)
|
|
{
|
|
ObjectDisposedException.ThrowIf(disposed, this);
|
|
byte[] data = new byte[96];
|
|
publicKey.CopyTo(data, 0);
|
|
seed.CopyTo(data, 32);
|
|
publicKey.CopyTo(data, 64);
|
|
try { PrivateFiles.Write(path, data); }
|
|
finally { CryptographicOperations.ZeroMemory(data); }
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (disposed) return;
|
|
disposed = true;
|
|
CryptographicOperations.ZeroMemory(seed);
|
|
}
|
|
}
|