Use random salt for each fingerprint key

Salt length depended on fingerprint name length, which lead to weak salts in some cases
It's now a fixed length (12 bytes)
This commit is contained in:
2021-06-05 12:20:14 +02:00
parent 3b1755b793
commit d7691e414b
5 changed files with 69 additions and 7 deletions

View File

@@ -2,6 +2,7 @@ using Akari.Prototype.Server.Cli.Commands;
using Akari.Prototype.Server.Options;
using Akari.Prototype.Server.Utils.Extensions;
using CliFx;
using Isopoh.Cryptography.Argon2;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

View File

@@ -6,10 +6,12 @@ using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
@@ -18,25 +20,75 @@ namespace Akari.Prototype.Server.Services
public sealed class AuthManager : IAuthManager, IDisposable
{
public const int AuthKeysLength = 256 / 8;
public const string SaltsPath = "auth_salts.json";
public IEnumerable<KeyValuePair<string, TimedEntry<AesGcm>>> Pairs => _keys;
private readonly ILogger<AuthManager> _logger;
private readonly IKeyManager _keyManager;
private readonly AkariPath _akariPath;
private IDictionary<string, TimedEntry<AesGcm>> _keys;
private IDictionary<string, string> _salts;
public AuthManager(ILogger<AuthManager> logger, IKeyManager keyManager)
public AuthManager(ILogger<AuthManager> logger, IKeyManager keyManager, AkariPath akariPath)
{
_logger = logger;
_keyManager = keyManager;
_akariPath = akariPath;
_keys = new ConcurrentDictionary<string, TimedEntry<AesGcm>>();
LoadSalts();
}
private void LoadSalts()
{
var path = _akariPath.GetPath(SaltsPath);
// Create new
if (!File.Exists(path))
{
_salts = new Dictionary<string, string>();
File.WriteAllText(path, JsonSerializer.Serialize(_salts));
}
// Load
else
{
_salts = JsonSerializer.Deserialize<IDictionary<string, string>>(File.ReadAllText(path));
}
}
private void SaveSalts()
{
var path = _akariPath.GetPath(SaltsPath);
File.WriteAllText(path, JsonSerializer.Serialize(_salts));
}
public void GenerateSalt(string name)
{
Span<byte> salt = Security.DefaultSaltLength <= 1024 ? stackalloc byte[Security.DefaultSaltLength]
: new byte[Security.DefaultSaltLength];
RandomNumberGenerator.Fill(salt);
_salts[name] = Convert.ToBase64String(salt);
SaveSalts();
}
public void Auth(string name, byte[] token)
{
// Retrieve salt
if (!_salts.TryGetValue(name, out var salt))
{
throw new Exception($"Can't find the salt for '{name}', try to register the fingerprint again");
}
// Derive key and store it
using var key = Security.Argon2idDeriveBytes(token, Encoding.UTF8.GetBytes(name), AuthKeysLength, clear: true);
using var key = Security.Argon2idDeriveBytes(token, Convert.FromBase64String(salt), AuthKeysLength, clear: true);
SetKey(name, new AesGcm(key.Buffer));
}

View File

@@ -67,6 +67,8 @@ namespace Akari.Prototype.Server.Services
{
SaveFingerprints();
_authManager.GenerateSalt(name);
return true;
}

View File

@@ -11,6 +11,8 @@ namespace Akari.Prototype.Server.Services
{
IEnumerable<KeyValuePair<string, TimedEntry<AesGcm>>> Pairs { get; }
void GenerateSalt(string name);
void Auth(string name, byte[] token);
bool Remove(string name);

View File

@@ -47,7 +47,7 @@ namespace Akari.Prototype.Server.Services
_logger.LogInformation($"Now listening on: {_listener.LocalEndpoint}");
return Task.CompletedTask;
return base.StartAsync(cancellationToken);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
@@ -126,7 +126,7 @@ namespace Akari.Prototype.Server.Services
position += read;
}
var text = Encoding.UTF8.GetString(data.Span);
var text = Encoding.UTF8.GetString(data[..position].Span);
var splitIndex = text.IndexOf('$');
_logger.LogDebug($"Received text: {text}");
@@ -138,9 +138,14 @@ namespace Akari.Prototype.Server.Services
var handle = GCHandle.Alloc(text, GCHandleType.Pinned);
_fingerprintManager.VerifyFingerprint(text[..splitIndex], Convert.FromBase64String(text[(splitIndex + 1)..]));
handle.Free();
try
{
_fingerprintManager.VerifyFingerprint(text[..splitIndex], Convert.FromBase64String(text[(splitIndex + 1)..]));
}
finally
{
handle.Free();
}
}
public override void Dispose()