From b3516162529cf876057fad37c5a155b6b097b0bd Mon Sep 17 00:00:00 2001 From: vnugent Date: Mon, 1 May 2023 17:23:31 -0400 Subject: Reduce pk storage footprint, login clause reorder & comments --- .../src/MFA/MFAUpgrade.cs | 15 ++----- .../src/MFA/UserMFAExtensions.cs | 50 ++++++++++++---------- 2 files changed, 31 insertions(+), 34 deletions(-) (limited to 'plugins/VNLib.Plugins.Essentials.Accounts/src/MFA') diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/MFAUpgrade.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/MFAUpgrade.cs index 5577d51..e69088a 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/MFAUpgrade.cs +++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/MFAUpgrade.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials.Accounts @@ -24,17 +24,15 @@ using System.Text.Json.Serialization; -#nullable enable - namespace VNLib.Plugins.Essentials.Accounts.MFA { - internal class MFAUpgrade + internal class MFAUpgrade : IClientSecInfo { /// /// The login's client id specifier /// [JsonPropertyName("cid")] - public string? ClientID { get; set; } + public string? ClientId { get; set; } /// /// The id of the user that is requesting a login /// @@ -50,16 +48,11 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA /// public key /// [JsonPropertyName("pubkey")] - public string? Base64PubKey { get; set; } + public string? PublicKey { get; set; } /// /// The user's specified language /// [JsonPropertyName("lang")] public string? ClientLocalLanguage { get; set; } - /// - /// The encrypted password token for the client - /// - [JsonPropertyName("cd")] - public string? PwClientData { get; set; } } } diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/UserMFAExtensions.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/UserMFAExtensions.cs index 63b2a2b..99f7fbb 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/UserMFAExtensions.cs +++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/UserMFAExtensions.cs @@ -23,12 +23,10 @@ */ using System; -using System.Text; using System.Linq; using System.Buffers; using System.Text.Json; using System.Collections.Generic; -using System.Security.Cryptography; using System.Text.Json.Serialization; using VNLib.Hashing; @@ -229,10 +227,16 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA return jwt.VerifyFromJwk(jwk); } - public static void PKISetUserKey(this IUser user, IReadOnlyDictionary? keyData) + public static void PKISetUserKey(this IUser user, IReadOnlyDictionary? keyFields) { + //Serialize the key data + byte[] keyData = JsonSerializer.SerializeToUtf8Bytes(keyFields, Statics.SR_OPTIONS); + + //convert to base32 string before writing user data + string base64 = Convert.ToBase64String(keyData); + //Store key data - user.SetObject(USER_PKI_ENTRY, keyData); + user[USER_PKI_ENTRY] = base64; } private static ReadOnlyJsonWebKey? RecoverKey(IUser user) @@ -248,9 +252,14 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA byte[] buffer = ArrayPool.Shared.Rent(JWK_KEY_BUFFER_SIZE); try { - //Recover bytes and get the jwk from the data - int encoded = Encoding.UTF8.GetBytes(keyData, buffer); - return new ReadOnlyJsonWebKey(buffer.AsSpan(0, encoded)); + //Recover base64 bytes from key data + ERRNO bytes = VnEncoding.TryFromBase64Chars(keyData, buffer); + if (!bytes) + { + return null; + } + //Recover json from the decoded binary data + return new ReadOnlyJsonWebKey(buffer.AsSpan(0, bytes)); } finally { @@ -293,7 +302,7 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA #endregion - private static HMAC GetSigningAlg(byte[] key) => new HMACSHA256(key); + private static HashAlg SigingAlg { get; } = HashAlg.SHA256; private static ReadOnlyMemory UpgradeHeader { get; } = CompileJwtHeader(); @@ -313,7 +322,6 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA /// /// /// The signed JWT upgrade message - /// The recovered upgrade /// The stored base64 encoded signature from the session that requested an upgrade /// True if the upgrade was verified, not expired, and was recovered from the signed message, false otherwise public static MFAUpgrade? RecoverUpgrade(this MFAConfig config, string upgradeJwtString, string base32Secret) @@ -325,10 +333,8 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA byte[] secret = VnEncoding.FromBase32String(base32Secret)!; try { - //Verify the - using HMAC hmac = GetSigningAlg(secret); - - if (!jwt.Verify(hmac)) + //Verify the signature + if (!jwt.Verify(secret, SigingAlg)) { return null; } @@ -364,7 +370,7 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA /// The message from the user requesting the login /// A signed upgrade message the client will pass back to the server after the MFA verification /// - public static Tuple? MFAGetUpgradeIfEnabled(this IUser user, MFAConfig? conf, LoginMessage login) + public static MfaUpgradeMessage? MFAGetUpgradeIfEnabled(this IUser user, MFAConfig? conf, LoginMessage login) { //Webauthn config @@ -383,8 +389,8 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA Type = MFAType.TOTP, //Store login message details UserName = login.UserName, - ClientID = login.ClientId, - Base64PubKey = login.ClientPublicKey, + ClientId = login.ClientId, + PublicKey = login.ClientPublicKey, ClientLocalLanguage = login.LocalLanguage, }; @@ -394,7 +400,7 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA return null; } - private static Tuple GetUpgradeMessage(MFAUpgrade upgrade, MFAConfig config) + private static MfaUpgradeMessage GetUpgradeMessage(MFAUpgrade upgrade, MFAConfig config) { //Add some random entropy to the upgrade message, to help prevent forgery string entropy = RandomHash.GetRandomBase32(config.NonceLenBytes); @@ -414,12 +420,8 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA //Generate a new random secret byte[] secret = RandomHash.GetRandomBytes(config.UpgradeKeyBytes); - //Init alg - using(HMAC alg = GetSigningAlg(secret)) - { - //sign jwt - upgradeJwt.Sign(alg); - } + //sign jwt + upgradeJwt.Sign(secret, SigingAlg); //compile and return jwt upgrade return new(upgradeJwt.Compile(), VnEncoding.ToBase32String(secret)); @@ -429,4 +431,6 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA public static string? MfaUpgradeSecret(this in SessionInfo session) => session[SESSION_SIG_KEY]; } + + readonly record struct MfaUpgradeMessage(string ClientJwt, string SessionKey); } -- cgit