diff options
author | vnugent <public@vaughnnugent.com> | 2023-05-01 17:23:31 -0400 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2023-05-01 17:23:31 -0400 |
commit | b3516162529cf876057fad37c5a155b6b097b0bd (patch) | |
tree | c1f8d990aa887e46e86da0bd169da1a8aea66d6e /plugins/VNLib.Plugins.Essentials.Accounts/src/MFA | |
parent | 0bafd510a0091960dbfe5ad08d3d524153117536 (diff) |
Reduce pk storage footprint, login clause reorder & comments
Diffstat (limited to 'plugins/VNLib.Plugins.Essentials.Accounts/src/MFA')
-rw-r--r-- | plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/MFAUpgrade.cs | 15 | ||||
-rw-r--r-- | plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/UserMFAExtensions.cs | 50 |
2 files changed, 31 insertions, 34 deletions
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 { /// <summary> /// The login's client id specifier /// </summary> [JsonPropertyName("cid")] - public string? ClientID { get; set; } + public string? ClientId { get; set; } /// <summary> /// The id of the user that is requesting a login /// </summary> @@ -50,16 +48,11 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA /// public key /// </summary> [JsonPropertyName("pubkey")] - public string? Base64PubKey { get; set; } + public string? PublicKey { get; set; } /// <summary> /// The user's specified language /// </summary> [JsonPropertyName("lang")] public string? ClientLocalLanguage { get; set; } - /// <summary> - /// The encrypted password token for the client - /// </summary> - [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<string, string>? keyData) + public static void PKISetUserKey(this IUser user, IReadOnlyDictionary<string, string>? 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<byte>.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<byte> UpgradeHeader { get; } = CompileJwtHeader(); @@ -313,7 +322,6 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA /// </summary> /// <param name="config"></param> /// <param name="upgradeJwtString">The signed JWT upgrade message</param> - /// <param name="upgrade">The recovered upgrade</param> /// <param name="base32Secret">The stored base64 encoded signature from the session that requested an upgrade</param> /// <returns>True if the upgrade was verified, not expired, and was recovered from the signed message, false otherwise</returns> 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 /// <param name="login">The message from the user requesting the login</param> /// <returns>A signed upgrade message the client will pass back to the server after the MFA verification</returns> /// <exception cref="InvalidOperationException"></exception> - public static Tuple<string, string>? 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<string, string> 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); } |