// System
using System;
// Unity
using UnityEngine;
// GUPS - AntiCheat - Core
using GUPS.AntiCheat.Core.Protected;
// GUPS - AntiCheat
using GUPS.AntiCheat.Detector;
using GUPS.AntiCheat.Settings;
namespace GUPS.AntiCheat.Protected
{
///
/// Represents a protected DateTime, designed to enhance integrity and security by obfuscating its value and incorporating anti-cheat measures.
/// In most cases, this protected DateTime can be used as a drop-in replacement for the default DateTime type.
///
[Serializable]
public struct ProtectedDateTime : IProtected, IDisposable, ISerializationCallbackReceiver
{
///
/// A struct does not have a default constructor that is called when the structure is created. Therefore, the protected primitive must return
/// a default value if it does not have an assigned value.
///
private bool isInitialized;
///
/// Gets a value indicating whether the protected value has integrity, i.e., whether it has maintained its original state.
///
private bool hasIntegrity;
///
/// Gets a value indicating whether the protected value has integrity, i.e., whether it has maintained its original state.
///
public bool HasIntegrity { get => hasIntegrity || !isInitialized; private set => hasIntegrity = value; }
///
/// The encrypted true value represented by a ProtectedInt64.
///
private ProtectedInt64 obfuscatedInt64;
///
/// A honeypot pretending to be the original value. If a user attempts to change this value via a cheat/hack engine, you will be notified.
/// The protected value will retain its true value.
///
[SerializeField]
private Int64 fakeValue;
///
/// Unity serialization hook. Ensures the correct values are serialized.
///
public void OnBeforeSerialize()
{
this.fakeValue = Value.Ticks;
}
///
/// Unity deserialization hook. Ensures the correct values are deserialized.
///
public void OnAfterDeserialize()
{
this = new DateTime(this.fakeValue);
}
///
/// Creates a new protected DateTime with the specified initial value.
///
/// The initial value of the protected DateTime.
public ProtectedDateTime(DateTime _Value)
{
// Initialization
this.isInitialized = true;
this.obfuscatedInt64 = 0;
this.fakeValue = 0;
this.hasIntegrity = true;
// Obfuscate the value.
this.Obfuscate(_Value);
}
///
/// Gets and sets the true unencrypted field value.
///
public DateTime Value
{
get
{
if (!this.isInitialized)
{
return new DateTime();
}
if (!this.CheckIntegrity())
{
AntiCheatMonitor.Instance.GetDetector()?.OnNext(this);
}
return this.UnObfuscate();
}
set { this.Obfuscate(value); }
}
///
/// Gets the true unencrypted field value.
///
object IProtected.Value => this.Value;
///
/// Obfuscates the specified value, encrypting it with the secret key.
///
private void Obfuscate(DateTime _Value)
{
// Obfuscate the value.
this.obfuscatedInt64.Value = _Value.Ticks;
// Assign the fake value.
this.fakeValue = _Value.Ticks;
}
///
/// Unobfuscates the secured value and returns the true unencrypted value.
///
/// The true unencrypted value.
private DateTime UnObfuscate()
{
// Get the unobfuscated value.
return new DateTime(this.obfuscatedInt64.Value);
}
///
/// Obfuscates the current value, generating a new random secret key.
///
public void Obfuscate()
{
// Obfuscate the value.
this.obfuscatedInt64.Obfuscate();
}
///
/// Checks the integrity of the protected DateTime, detecting if an attacher changed the honeypot fake value.
///
/// True if the protected DateTime has integrity; otherwise, false.
public bool CheckIntegrity()
{
// Unobfuscate the secured value.
DateTime var_UnobfuscatedValue = this.UnObfuscate();
// Check if an attacher changed the honeypot fake value.
if (this.fakeValue != var_UnobfuscatedValue.Ticks)
{
this.HasIntegrity = false;
}
// Return the integrity status.
return this.HasIntegrity;
}
///
/// Disposes of the secure and secret values.
///
public void Dispose()
{
this.obfuscatedInt64.Dispose();
}
///
/// Converts the protected DateTime to its string representation.
///
/// The string representation of the true value.
public override string ToString()
{
return this.Value.ToString();
}
///
/// Gets the hash code of the protected DateTime's true value.
///
/// The hash code of the true value.
public override int GetHashCode()
{
return this.Value.GetHashCode();
}
#region Implicit operator
///
/// Implicitly converts a DateTime value to a protected DateTime.
///
/// The DateTime value to convert.
/// The corresponding protected DateTime.
public static implicit operator ProtectedDateTime(DateTime _Value)
{
return new ProtectedDateTime(_Value);
}
///
/// Implicitly converts a protected DateTime to its DateTime value.
///
/// The protected DateTime to convert.
/// The DateTime value of the protected DateTime.
public static implicit operator DateTime(ProtectedDateTime _Value)
{
return _Value.Value;
}
#endregion
#region Equality operator
///
/// Checks if two protected DateTimes are equal based on their true values.
///
/// The first protected DateTime.
/// The second protected DateTime.
/// True if the true values are equal; otherwise, false.
public static bool operator ==(ProtectedDateTime v1, ProtectedDateTime v2)
{
return v1.Value == v2.Value;
}
///
/// Checks if two protected DateTimes are not equal based on their true values.
///
/// The first protected DateTime.
/// The second protected DateTime.
/// True if the true values are not equal; otherwise, false.
public static bool operator !=(ProtectedDateTime v1, ProtectedDateTime v2)
{
return v1.Value != v2.Value;
}
///
/// Checks if the protected DateTime is equal to another object based on their true values.
///
/// The object to compare with the protected DateTime.
/// True if the true values are equal; otherwise, false.
public override bool Equals(object obj)
{
if (obj is ProtectedDateTime)
{
return this.Value == ((ProtectedDateTime)obj).Value;
}
return this.Value.Equals(obj);
}
#endregion
}
}