using EnvelopeGenerator.Domain.Constants; using System.Text; namespace EnvelopeGenerator.Application.Common.Extensions; /// /// /// public static class DecodingExtensions { /// /// Validates whether a given string is a correctly formatted Base-64 encoded string. /// /// /// This method checks the string for proper Base-64 formatting, which includes validating /// the length of the string (must be divisible by 4). It also checks each character to ensure /// it belongs to the Base-64 character set (A-Z, a-z, 0-9, '+', '/', and '=' for padding). /// The method ensures that padding characters ('=') only appear at the end of the string and /// are in a valid configuration (either one '=' at the end if the string's length % 4 is 3, /// or two '==' if the length % 4 is 2). /// /// The Base-64 encoded string to validate. /// /// true if the string is a valid Base-64 encoded string; otherwise, false. /// /// /// /// string testString = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnk="; /// bool isValid = IsValidBase64String(testString); /// Console.WriteLine(isValid); // Output: true /// /// public static bool IsBase64String(this string input) { // Check if the string is null or empty if (string.IsNullOrEmpty(input)) { return false; } // Replace valid base-64 padding input = input.Trim(); int mod4 = input.Length % 4; if (mod4 > 0) { // Base-64 string lengths should be divisible by 4 return false; } // Check each character to ensure it is valid base-64 foreach (char c in input) { if (!char.IsLetterOrDigit(c) && c != '+' && c != '/' && c != '=') { // Invalid character detected return false; } } // Ensure no invalid padding scenarios exist if (input.EndsWith("==") && input.Length % 4 == 0 || input.EndsWith("=") && input.Length % 4 == 3) { return true; } return input.IndexOf('=') == -1; // No padding allowed except at the end } /// /// /// /// /// /// public static bool TryDecode(this string encodedKey, out string[] decodedKeys) { try { byte[] bytes = Convert.FromBase64String(encodedKey); string decodedString = Encoding.UTF8.GetString(bytes); decodedKeys = decodedString.Split(new string[] { "::" }, StringSplitOptions.None); return true; } catch(ArgumentNullException) { } catch (FormatException) { } catch(ArgumentException) { } decodedKeys = Array.Empty(); return false; } /// /// /// /// /// public static EncodeType GetEncodeType(this string[] decodedKeys) => decodedKeys.Length switch { 2 => EncodeType.EnvelopeReceiver, 3 => long.TryParse(decodedKeys[1], out var _) ? EncodeType.EnvelopeReceiverReadOnly : EncodeType.Undefined, _ => EncodeType.Undefined, }; /// /// /// /// /// /// public static (string? EnvelopeUuid, string? ReceiverSignature) ParseEnvelopeReceiverId(this string[] decodedKeys) => decodedKeys.GetEncodeType() == EncodeType.EnvelopeReceiver ? (EnvelopeUuid: decodedKeys[0], ReceiverSignature: decodedKeys[1]) : throw new InvalidOperationException("Attempted to convert a decoded other than type EnvelopeReceiver to EnvelopeReceiver."); /// /// /// /// /// /// public static long ParseReadOnlyId(this string[] decodedKeys) => decodedKeys.GetEncodeType() == EncodeType.EnvelopeReceiverReadOnly ? long.Parse(decodedKeys[1]) : throw new InvalidOperationException("Attempted to convert a decoded other than type EnvelopeReceiver to EnvelopeReceiver. "); /// /// Decodes the envelope receiver ID and extracts the envelope UUID and receiver signature. /// /// The base64 encoded string containing the envelope UUID and receiver signature. /// A tuple containing the envelope UUID and receiver signature. public static (string? EnvelopeUuid, string? ReceiverSignature) DecodeEnvelopeReceiverId(this string envelopeReceiverId) { if (!envelopeReceiverId.IsBase64String()) { return (null, null); } byte[] bytes = Convert.FromBase64String(envelopeReceiverId); string decodedString = Encoding.UTF8.GetString(bytes); string[] parts = decodedString.Split(new string[] { "::" }, StringSplitOptions.None); if (parts.Length > 1) return (EnvelopeUuid: parts[0], ReceiverSignature: parts[1]); else return (string.Empty, string.Empty); } /// /// /// /// /// public static long? DecodeEnvelopeReceiverReadOnlyId(this string envelopeReceiverReadOnlyId) { if (!envelopeReceiverReadOnlyId.IsBase64String()) { return null; } byte[] bytes = Convert.FromBase64String(envelopeReceiverReadOnlyId); string decodedString = Encoding.UTF8.GetString(bytes); string[] parts = decodedString.Split(new string[] { "::" }, StringSplitOptions.None); if (parts.Length > 2) return long.TryParse(parts[1], out long readOnlyId) ? readOnlyId : null; else return null; } /// /// Gets the envelope UUID from the decoded envelope receiver ID. /// /// The base64 encoded string to decode. /// The envelope UUID. public static string? GetEnvelopeUuid(this string envelopeReceiverId) => envelopeReceiverId.DecodeEnvelopeReceiverId().EnvelopeUuid; /// /// Gets the receiver signature from the decoded envelope receiver ID. /// /// The base64 encoded string to decode. /// The receiver signature. public static string? GetReceiverSignature(this string envelopeReceiverId) => envelopeReceiverId.DecodeEnvelopeReceiverId().ReceiverSignature; }