From 3278fa8bfcd62a429fe682cec88653920991d39e Mon Sep 17 00:00:00 2001 From: jvyden Date: Sun, 23 Jul 2023 22:49:10 -0400 Subject: [PATCH] Refactor verification process, update ticket structure and improve reader --- NPTicket.Test/Program.cs | 2 +- NPTicket/Reader/TicketReader.cs | 41 ++++++++++++++------ NPTicket/Ticket.cs | 44 +++++++++++++++------- NPTicket/Types/TicketData.cs | 13 +++++++ NPTicket/Types/TicketDataHeader.cs | 13 ------- NPTicket/Types/TicketDataSection.cs | 15 ++++++++ NPTicket/Types/TicketDataSectionType.cs | 8 ++++ NPTicket/Types/TicketDataType.cs | 13 +++++++ NPTicket/Verification/TicketSection.cs | 15 -------- NPTicket/Verification/TicketSectionType.cs | 6 --- NPTicket/Verification/TicketVerifier.cs | 14 ++++--- 11 files changed, 118 insertions(+), 66 deletions(-) create mode 100644 NPTicket/Types/TicketData.cs delete mode 100644 NPTicket/Types/TicketDataHeader.cs create mode 100644 NPTicket/Types/TicketDataSection.cs create mode 100644 NPTicket/Types/TicketDataSectionType.cs create mode 100644 NPTicket/Types/TicketDataType.cs delete mode 100644 NPTicket/Verification/TicketSection.cs delete mode 100644 NPTicket/Verification/TicketSectionType.cs diff --git a/NPTicket.Test/Program.cs b/NPTicket.Test/Program.cs index 06f927d..28f8468 100644 --- a/NPTicket.Test/Program.cs +++ b/NPTicket.Test/Program.cs @@ -8,6 +8,6 @@ Console.WriteLine(JsonSerializer.Serialize(ticket)); -TicketVerifier verifier = new(ticket, RpcnSigningKey.Instance); +TicketVerifier verifier = new(ticketData, ticket, RpcnSigningKey.Instance); Console.WriteLine(JsonSerializer.Serialize(verifier)); Console.WriteLine(verifier.IsTicketValid()); \ No newline at end of file diff --git a/NPTicket/Reader/TicketReader.cs b/NPTicket/Reader/TicketReader.cs index a838905..6e7dd74 100644 --- a/NPTicket/Reader/TicketReader.cs +++ b/NPTicket/Reader/TicketReader.cs @@ -1,7 +1,6 @@ using System.Buffers.Binary; using System.Text; using NPTicket.Types; -using NPTicket.Verification; namespace NPTicket.Reader; @@ -33,31 +32,51 @@ internal ushort ReadTicketHeader() return ReadUInt16(); // Ticket length } - internal TicketSection ReadTicketSectionHeader() + internal TicketDataSection ReadTicketSectionHeader() { this.ReadByte(); // Skip first byte of type (which is a short) - TicketSectionType type = (TicketSectionType)this.ReadByte(); + TicketDataSectionType type = (TicketDataSectionType)this.ReadByte(); ushort length = this.ReadUInt16(); long position = this.BaseStream.Position; - return new TicketSection(type, length, (uint)position); + return new TicketDataSection(type, length, position); } - private TicketDataHeader ReadTicketDataHeader() => new(ReadUInt16(), ReadUInt16()); + private TicketData ReadTicketData(TicketDataType expectedType) + { + TicketData data = new TicketData((TicketDataType)ReadUInt16(), ReadUInt16()); + if (data.Type != expectedType && expectedType != TicketDataType.Empty) + throw new FormatException($"Expected data type to be {expectedType}, was really {data.Type} ({(int)data.Type})"); + + return data; + } - private byte[] ReadTicketByteArray() => ReadBytes(ReadTicketDataHeader().Length); - internal string ReadTicketString() => Encoding.Default.GetString(ReadTicketByteArray()).TrimEnd('\0'); + internal byte[] ReadTicketBinaryData(TicketDataType type = TicketDataType.Binary) + => ReadBytes(ReadTicketData(type).Length); + internal string ReadTicketStringData(TicketDataType type = TicketDataType.String) + => Encoding.Default.GetString(ReadTicketBinaryData(type)).TrimEnd('\0'); - internal uint ReadTicketUInt32() + internal uint ReadTicketUInt32Data() { - ReadTicketDataHeader(); + ReadTicketData(TicketDataType.UInt32); return ReadUInt32(); } - internal ulong ReadTicketUInt64() + internal ulong ReadTicketUInt64Data() { - ReadTicketDataHeader(); + ReadTicketData(TicketDataType.UInt64); return ReadUInt64(); } + + internal DateTimeOffset ReadTicketTimestampData() + { + ReadTicketData(TicketDataType.Timestamp); + return DateTimeOffset.FromUnixTimeMilliseconds((long)ReadUInt64()); + } + + internal void SkipTicketEmptyData(int sections = 1) + { + for (int i = 0; i < sections; i++) ReadTicketData(TicketDataType.Empty); + } } \ No newline at end of file diff --git a/NPTicket/Ticket.cs b/NPTicket/Ticket.cs index 15ec1ee..fd7ca25 100644 --- a/NPTicket/Ticket.cs +++ b/NPTicket/Ticket.cs @@ -24,8 +24,8 @@ private Ticket() {} public string SerialId { get; set; } public uint IssuerId { get; set; } - public ulong IssuedDate { get; set; } - public ulong ExpiryDate { get; set; } + public DateTimeOffset IssuedDate { get; set; } + public DateTimeOffset ExpiryDate { get; set; } public ulong UserId { get; set; } public string Username { get; set; } @@ -38,7 +38,10 @@ private Ticket() {} public uint Status { get; set; } public ushort TicketLength { get; set; } - public TicketSection BodySection { get; set; } + public TicketDataSection BodySection { get; set; } + + public string SignatureIdentifier { get; set; } + public byte[] SignatureData { get; set; } // TODO: Use GeneratedRegex, this is not in netstandard yet private static readonly Regex ServiceIdRegex = new("(?<=-)[A-Z0-9]{9}(?=_)", RegexOptions.Compiled); @@ -68,30 +71,43 @@ public static Ticket ReadFromStream(Stream stream) ticket.BodySection = reader.ReadTicketSectionHeader(); - if (ticket.BodySection.Type != TicketSectionType.Body) + if (ticket.BodySection.Type != TicketDataSectionType.Body) { - throw new FormatException($"Expected first section to be {nameof(TicketSectionType.Body)}, " + + throw new FormatException($"Expected first section to be {nameof(TicketDataSectionType.Body)}, " + $"was really {ticket.BodySection.Type} ({(int)ticket.BodySection.Type})"); } - ticket.SerialId = reader.ReadTicketString(); + ticket.SerialId = reader.ReadTicketStringData(TicketDataType.Binary); - ticket.IssuerId = reader.ReadTicketUInt32(); + ticket.IssuerId = reader.ReadTicketUInt32Data(); - ticket.IssuedDate = reader.ReadTicketUInt64(); - ticket.ExpiryDate = reader.ReadTicketUInt64(); + ticket.IssuedDate = reader.ReadTicketTimestampData(); + ticket.ExpiryDate = reader.ReadTicketTimestampData(); - ticket.UserId = reader.ReadTicketUInt64(); - ticket.Username = reader.ReadTicketString(); + ticket.UserId = reader.ReadTicketUInt64Data(); + ticket.Username = reader.ReadTicketStringData(); - ticket.Country = reader.ReadTicketString(); // No I am not going to brazil - ticket.Domain = reader.ReadTicketString(); + ticket.Country = reader.ReadTicketStringData(TicketDataType.Binary); // No I am not going to brazil + ticket.Domain = reader.ReadTicketStringData(); - ticket.ServiceId = reader.ReadTicketString(); + ticket.ServiceId = reader.ReadTicketStringData(TicketDataType.Binary); ticket.TitleId = ServiceIdRegex.Matches(ticket.ServiceId)[0].ToString(); ticket.Status = reader.ReadUInt32(); + // Skip padding section in ticket + reader.SkipTicketEmptyData(3); + + TicketDataSection footer = reader.ReadTicketSectionHeader(); + if (footer.Type != TicketDataSectionType.Footer) + { + throw new FormatException($"Expected last section to be {nameof(TicketDataSectionType.Footer)}, " + + $"was really {footer.Type} ({(int)footer.Type})"); + } + + ticket.SignatureIdentifier = reader.ReadTicketStringData(TicketDataType.Binary); + ticket.SignatureData = reader.ReadTicketBinaryData(); + return ticket; } } \ No newline at end of file diff --git a/NPTicket/Types/TicketData.cs b/NPTicket/Types/TicketData.cs new file mode 100644 index 0000000..897ebf7 --- /dev/null +++ b/NPTicket/Types/TicketData.cs @@ -0,0 +1,13 @@ +namespace NPTicket.Types; + +public readonly struct TicketData +{ + public readonly TicketDataType Type; + public readonly ushort Length; + + public TicketData(TicketDataType type, ushort length) + { + Type = type; + Length = length; + } +} \ No newline at end of file diff --git a/NPTicket/Types/TicketDataHeader.cs b/NPTicket/Types/TicketDataHeader.cs deleted file mode 100644 index a83218b..0000000 --- a/NPTicket/Types/TicketDataHeader.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace NPTicket.Types; - -public readonly struct TicketDataHeader -{ - public readonly ushort Type; - public readonly ushort Length; - - public TicketDataHeader(ushort type, ushort length) - { - Type = type; - Length = length; - } -} \ No newline at end of file diff --git a/NPTicket/Types/TicketDataSection.cs b/NPTicket/Types/TicketDataSection.cs new file mode 100644 index 0000000..73c498a --- /dev/null +++ b/NPTicket/Types/TicketDataSection.cs @@ -0,0 +1,15 @@ +namespace NPTicket.Types; + +public readonly struct TicketDataSection +{ + public TicketDataSectionType Type { get; } + public ushort Length { get; } + public int Position { get; } + + public TicketDataSection(TicketDataSectionType type, ushort length, long position) + { + Type = type; + Length = length; + Position = (int)position; + } +} \ No newline at end of file diff --git a/NPTicket/Types/TicketDataSectionType.cs b/NPTicket/Types/TicketDataSectionType.cs new file mode 100644 index 0000000..0662587 --- /dev/null +++ b/NPTicket/Types/TicketDataSectionType.cs @@ -0,0 +1,8 @@ +namespace NPTicket.Types; + +public enum TicketDataSectionType : byte +{ + Body = 0, + + Footer = 2, +} \ No newline at end of file diff --git a/NPTicket/Types/TicketDataType.cs b/NPTicket/Types/TicketDataType.cs new file mode 100644 index 0000000..a244c53 --- /dev/null +++ b/NPTicket/Types/TicketDataType.cs @@ -0,0 +1,13 @@ +namespace NPTicket.Types; + +public enum TicketDataType : ushort +{ + Empty = 0, + UInt32 = 1, + UInt64 = 2, + + String = 4, + + Timestamp = 7, + Binary = 8, +} \ No newline at end of file diff --git a/NPTicket/Verification/TicketSection.cs b/NPTicket/Verification/TicketSection.cs deleted file mode 100644 index 57ef1fe..0000000 --- a/NPTicket/Verification/TicketSection.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace NPTicket.Verification; - -public readonly struct TicketSection -{ - public TicketSectionType Type { get; } - public ushort Length { get; } - public uint Position { get; } - - public TicketSection(TicketSectionType type, ushort length, uint position) - { - Type = type; - Length = length; - Position = position; - } -} \ No newline at end of file diff --git a/NPTicket/Verification/TicketSectionType.cs b/NPTicket/Verification/TicketSectionType.cs deleted file mode 100644 index 7a58e5a..0000000 --- a/NPTicket/Verification/TicketSectionType.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace NPTicket.Verification; - -public enum TicketSectionType : byte -{ - Body = 0, -} \ No newline at end of file diff --git a/NPTicket/Verification/TicketVerifier.cs b/NPTicket/Verification/TicketVerifier.cs index a47c7a4..6e201de 100644 --- a/NPTicket/Verification/TicketVerifier.cs +++ b/NPTicket/Verification/TicketVerifier.cs @@ -9,12 +9,13 @@ namespace NPTicket.Verification; public class TicketVerifier { - private Ticket _ticket; - // private ECPublicKeyParameters _publicKey; - private ISigner _signer; + private readonly Ticket _ticket; + private readonly byte[] _ticketData; + private readonly ISigner _signer; - public TicketVerifier(Ticket ticket, ITicketSigningKey key) + public TicketVerifier(byte[] ticketData, Ticket ticket, ITicketSigningKey key) { + this._ticketData = ticketData; this._ticket = ticket; X9ECParameters xParams = ECNamedCurveTable.GetByName(key.CurveTable); @@ -23,11 +24,12 @@ public TicketVerifier(Ticket ticket, ITicketSigningKey key) ECPublicKeyParameters publicKey = new ECPublicKeyParameters(ecPoint, domainParams); this._signer = SignerUtilities.GetSigner(key.HashAlgorithm + "withECDSA"); - _signer.Init(false, publicKey); + this._signer.Init(false, publicKey); } public bool IsTicketValid() { - return false; + this._signer.BlockUpdate(this._ticketData, this._ticket.BodySection.Position, this._ticket.BodySection.Length); + return this._signer.VerifySignature(this._ticketData); } } \ No newline at end of file