using System; using System.Collections.Immutable; using System.Linq; using System.Text; namespace Barotrauma; public static class UnixTime { public static readonly DateTime UtcEpoch = new DateTime(year: 1970, month: 1, day: 1, hour: 0, minute: 0, second: 0, kind: DateTimeKind.Utc); public static Option ParseUtc(string str) { if (!ulong.TryParse(str, out var seconds)) { return Option.None; } return Option.Some(UtcEpoch + TimeSpan.FromSeconds(seconds)); } } /// /// URL-safe Base64. See https://datatracker.ietf.org/doc/html/rfc4648#section-5 /// public static class Base64Url { public static bool IsBase64Url(this string str) => str.All(c => c is (>= 'A' and <= 'Z') or (>= 'a' and <= 'z') or (>= '0' and <= '9') or '-' or '_'); public static Option DecodeUtf8String(string encodedForm) { return DecodeBytes(encodedForm).Select(bytes => Encoding.UTF8.GetString(bytes.AsSpan())); } public static Option> DecodeBytes(string encodedForm) { if (!encodedForm.IsBase64Url()) { return Option.None; } string base64Form = encodedForm.Replace("-", "+").Replace("_", "/"); base64Form += new string('=', (4 - (base64Form.Length % 4)) % 4); return Option.Some(Convert.FromBase64String(base64Form).ToImmutableArray()); } } /// /// Rudimentary JSON Web Token implementation. See https://en.wikipedia.org/wiki/JSON_Web_Token. /// This is used by continuance tokens and ID tokens as part of their internal representation. /// We can use the data encoded in them for some things, such as determining a token's expiry time. /// public readonly record struct JsonWebToken( string Header, string Payload, string Signature) { public bool IsValid => Header.IsBase64Url() && Payload.IsBase64Url() && Signature.IsBase64Url(); public override string ToString() => $"{Header}.{Payload}.{Signature}"; public string HeaderDecoded => Base64Url.DecodeUtf8String(Header).Fallback(""); public string PayloadDecoded => Base64Url.DecodeUtf8String(Payload).Fallback(""); public static Option Parse(string str) { if (str.Split(".") is not { Length: 3 } split) { return Option.None; } var newToken = new JsonWebToken( Header: split[0], Payload: split[1], Signature: split[2]); if (!newToken.IsValid) { return Option.None; } return Option.Some(newToken); } }