public string ToString(bool omitExtendedChecksum) { var result = string.Empty; if (PayloadType.HasFlag(PayloadType.AddressedByName)) { result += Pascal64Encoding.Escape(AccountName); } else { result += Account.ToString(); if (AccountChecksum != null) { result += $"-{AccountChecksum}"; } } var payloadContent = string.Empty; if (PayloadType.HasFlag(PayloadType.AsciiFormatted)) { payloadContent = $@"""{PascalAsciiEncoding.Escape(Payload)}"""; } else if (PayloadType.HasFlag(PayloadType.HexFormatted)) { payloadContent = $@"0x{Payload}"; } else if (PayloadType.HasFlag(PayloadType.Base58Formatted)) { payloadContent = $"{Payload}"; } else { // it is non-deterministic, so payload content is ignored payloadContent = string.Empty; } if (PayloadType.HasFlag(PayloadType.Public)) { result += $"[{payloadContent}]"; } else if (PayloadType.HasFlag(PayloadType.RecipientKeyEncrypted)) { result += $"({payloadContent})"; } else if (PayloadType.HasFlag(PayloadType.SenderKeyEncrypted)) { result += $"<{payloadContent}>"; } else if (PayloadType.HasFlag(PayloadType.PasswordEncrypted)) { result += $"{{{payloadContent}:{PascalAsciiEncoding.Escape(Password)}}}"; } else { // it is non-deterministic, so payload omitted entirely } if (ExtendedChecksum != null && !omitExtendedChecksum) { result += $":{ExtendedChecksum}"; } return(result); }
public override bool TryParse(string epasaText, out EPasa epasa, out EPasaErrorCode errorCode) { errorCode = EPasaErrorCode.Success; epasa = new EPasa(); if (string.IsNullOrEmpty(epasaText)) { errorCode = EPasaErrorCode.BadFormat; return(false); } var match = _epasaRegex.Match(epasaText); var checksumDelim = match.Groups["ChecksumDelim"].Success ? match.Groups["ChecksumDelim"].Value : null; var accountNumber = match.Groups["AccountNumber"].Success ? match.Groups["AccountNumber"].Value : null; var accountChecksum = match.Groups["Checksum"].Success ? match.Groups["Checksum"].Value : null; var accountName = match.Groups["AccountName"].Success ? match.Groups["AccountName"].Value : null; var payloadStartChar = match.Groups["PayloadStartChar"].Success ? match.Groups["PayloadStartChar"].Value : null; var payloadEndChar = match.Groups["PayloadEndChar"].Success ? match.Groups["PayloadEndChar"].Value : null; var payloadContent = match.Groups["PayloadContent"].Success ? match.Groups["PayloadContent"].Value : null; var payloadPasswordDelim = match.Groups["PayloadPasswordDelim"].Success ? match.Groups["PayloadPasswordDelim"].Value : null; var payloadPassword = match.Groups["PayloadPassword"].Success ? match.Groups["PayloadPassword"].Value : null; var extendedChecksumDelim = match.Groups["ExtendedChecksumDelim"].Success ? match.Groups["ExtendedChecksumDelim"].Value : null; var extendedChecksum = match.Groups["ExtendedChecksum"].Success ? match.Groups["ExtendedChecksum"].Value : null; // Check parsed completely if (epasaText != match.Value) { errorCode = EPasaErrorCode.BadFormat; return(false); } if (accountName != null) { // Account Name if (string.IsNullOrEmpty(accountName)) { errorCode = EPasaErrorCode.BadFormat; return(false); } epasa.PayloadType = epasa.PayloadType | PayloadType.AddressedByName; epasa.AccountName = Pascal64Encoding.Unescape(accountName); epasa.Account = epasa.AccountChecksum = null; } else { // Account Number if (!uint.TryParse(accountNumber, out var accNo)) { errorCode = EPasaErrorCode.InvalidAccountNumber; return(false); } epasa.Account = accNo; var actualAccountChecksum = AccountHelper.CalculateAccountChecksum(accNo); if (checksumDelim != null) { // validate account checksum if (!uint.TryParse(accountChecksum, out var accChecksum)) { errorCode = EPasaErrorCode.AccountChecksumInvalid; return(false); } if (accChecksum != actualAccountChecksum) { errorCode = EPasaErrorCode.BadChecksum; return(false); } } epasa.AccountChecksum = actualAccountChecksum; } // Encryption type switch (payloadStartChar) { case null: break; case "[": if (payloadEndChar != "]") { errorCode = EPasaErrorCode.MismatchedPayloadEncoding; return(false); } epasa.PayloadType = epasa.PayloadType | PayloadType.Public; break; case "(": if (payloadEndChar != ")") { errorCode = EPasaErrorCode.MismatchedPayloadEncoding; return(false); } epasa.PayloadType = epasa.PayloadType | PayloadType.RecipientKeyEncrypted; break; case "<": if (payloadEndChar != ">") { errorCode = EPasaErrorCode.MismatchedPayloadEncoding; return(false); } epasa.PayloadType = epasa.PayloadType | PayloadType.SenderKeyEncrypted; break; case "{": if (payloadEndChar != "}") { errorCode = EPasaErrorCode.MismatchedPayloadEncoding; return(false); } epasa.PayloadType = epasa.PayloadType | PayloadType.PasswordEncrypted; break; default: throw new NotSupportedException($"Unrecognized start character '{payloadStartChar}'"); } // Password if (epasa.PayloadType.HasFlag(PayloadType.PasswordEncrypted)) { if (payloadPasswordDelim == null) { errorCode = EPasaErrorCode.MissingPassword; return(false); } epasa.Password = PascalAsciiEncoding.Unescape(payloadPassword ?? ""); } else if (payloadPasswordDelim != null) { errorCode = EPasaErrorCode.UnusedPassword; return(false); } // Payload if (payloadStartChar != null) { if (payloadContent == null) { epasa.Payload = string.Empty; } else if (payloadContent.StartsWith("\"")) { epasa.PayloadType = epasa.PayloadType | PayloadType.AsciiFormatted; epasa.Payload = PascalAsciiEncoding.Unescape(payloadContent.Trim('"')); } else if (payloadContent.StartsWith("0x")) { epasa.PayloadType = epasa.PayloadType | PayloadType.HexFormatted; epasa.Payload = payloadContent.Substring(2); } else { epasa.PayloadType = epasa.PayloadType | PayloadType.Base58Formatted; epasa.Payload = payloadContent; } } // Payload Lengths if (!EPasaHelper.IsValidPayloadLength(epasa.PayloadType, epasa.Payload)) { errorCode = EPasaErrorCode.PayloadTooLarge; return(false); } // Extended Checksum var actualChecksum = EPasaHelper.ComputeExtendedChecksum(epasa.ToString(true)); if (extendedChecksumDelim != null) { if (extendedChecksum != actualChecksum) { errorCode = EPasaErrorCode.BadExtendedChecksum; return(false); } } epasa.ExtendedChecksum = actualChecksum; return(true); }
private EPasaErrorCode TryCompile(EPasaNode epasaNode, EPasa epasa) { EPasaErrorCode TryCompileAccount(AccountNode accountNode) { switch (accountNode.Type) { case TokenType.AccountName: if (!Pascal64Encoding.IsValidEscaped(accountNode.Name.Value)) { return(EPasaErrorCode.InvalidAccountName); } epasa.AccountName = Pascal64Encoding.Unescape(accountNode.Name.Value); epasa.PayloadType = epasa.PayloadType.SetFlags(PayloadType.AddressedByName); break; case TokenType.AccountNumber: if (!uint.TryParse(accountNode.AccountNo.Value, out var accNo)) { return(EPasaErrorCode.BadFormat); } if (accountNode.AccountNo.Value.StartsWith("0") && accNo != 0) { return(EPasaErrorCode.BadFormat); } epasa.Account = accNo; var actualChecksum = AccountHelper.CalculateAccountChecksum(accNo); if (accountNode.Checksum != null) { if (!uint.TryParse(accountNode.Checksum.Value, out var checksum)) { return(EPasaErrorCode.BadFormat); } if (checksum != actualChecksum) { return(EPasaErrorCode.AccountChecksumInvalid); } } epasa.AccountChecksum = actualChecksum; break; default: throw new ArgumentOutOfRangeException(nameof(accountNode)); } return(EPasaErrorCode.Success); } EPasaErrorCode TryCompilePayload(PayloadNode payloadNode) { epasa.PayloadType = epasa.PayloadType | payloadNode.Encryption; if (payloadNode.Content != null) { switch (payloadNode.Content.Type) { case TokenType.PascalAsciiString: epasa.PayloadType = epasa.PayloadType | PayloadType.AsciiFormatted; epasa.Payload = PascalAsciiEncoding.Unescape(payloadNode.Content.Value); break; case TokenType.Base58String: epasa.PayloadType = epasa.PayloadType | PayloadType.Base58Formatted; epasa.Payload = payloadNode.Content.Value; break; case TokenType.HexString: epasa.PayloadType = epasa.PayloadType | PayloadType.HexFormatted; epasa.Payload = payloadNode.Content.Value; break; default: throw new ArgumentOutOfRangeException(nameof(payloadNode.Content.Type)); } if (!EPasaHelper.IsValidPayloadLength(epasa.PayloadType, epasa.Payload)) { return(EPasaErrorCode.PayloadTooLarge); } } if (payloadNode.Password != null) { if (!EPasaHelper.IsValidPasswordLength(payloadNode.Password.Value)) { return(EPasaErrorCode.InvalidPassword); } epasa.Password = PascalAsciiEncoding.Unescape(payloadNode.Password.Value); } return(EPasaErrorCode.Success); } EPasaErrorCode TryCompileExtendedChecksum(ValueNode extendedChecksumNode) { var actualExtendedChecksum = EPasaHelper.ComputeExtendedChecksum(epasa.ToString(true)); if (extendedChecksumNode != null && extendedChecksumNode.Value != actualExtendedChecksum) { return(EPasaErrorCode.AccountChecksumInvalid); } epasa.ExtendedChecksum = actualExtendedChecksum; return(EPasaErrorCode.Success); } var result = TryCompileAccount(epasaNode.Account); if (result != EPasaErrorCode.Success) { return(result); } if (epasaNode.Payload != null) { result = TryCompilePayload(epasaNode.Payload); if (result != EPasaErrorCode.Success) { return(result); } } result = TryCompileExtendedChecksum(epasaNode.ExtendedChecksum); if (result != EPasaErrorCode.Success) { return(result); } return(result); }