// 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 decimal designed to enhance data integrity and security by obfuscating its value and incorporating anti-cheat measures. /// In most cases, this protected decimal can be used as a drop-in replacement for the default decimal type. /// [Serializable] public struct ProtectedDecimal : 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 as an array of integers. /// private int[] obfuscatedValues; /// /// A honeypot pretending to be the original value. If a user attempts to change this value via a cheat/hack engine, notifications will be triggered. /// The protected value will retain its true value. /// [SerializeField] private double fakeValue; /// /// Unity serialization hook. Ensures the correct values will be serialized. /// public void OnBeforeSerialize() { this.fakeValue = (double)this.Value; } /// /// Unity deserialization hook. Ensures the correct values will be deserialized. /// public void OnAfterDeserialize() { this = (decimal)this.fakeValue; } /// /// Initializes a new instance of the struct with the specified initial value. /// /// The initial value of the protected decimal. public ProtectedDecimal(decimal _Value = 0) { // Initialization this.isInitialized = true; this.obfuscatedValues = decimal.GetBits(_Value); this.hasIntegrity = true; // Setup fake value. this.fakeValue = (double)_Value; } /// /// Gets and sets the true unencrypted field value. /// public decimal Value { get { if (!this.isInitialized) { return 0; } 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. /// /// The value to be obfuscated. private void Obfuscate(decimal _Value) { // Obfuscate the value. this.obfuscatedValues = decimal.GetBits(_Value); // Assign the fake value. this.fakeValue = (double)_Value; } /// /// Unobfuscates the secured value and returns the true unencrypted value. /// /// The true unencrypted value. private decimal UnObfuscate() { // Get the unobfuscated value. return new decimal(this.obfuscatedValues); } /// /// Obfuscates the current value, generating a new random secret key. /// public void Obfuscate() { // Unobfuscate the secured value. decimal var_UnobfuscatedValue = this.UnObfuscate(); // Obfuscate the value. this.Obfuscate(var_UnobfuscatedValue); } /// /// Checks the integrity of the protected value, detecting if an attacker changed the honeypot fake value. /// /// True if the protected value has integrity; otherwise, false. public bool CheckIntegrity() { // Unobfuscate the secured value. decimal var_UnobfuscatedValue = this.UnObfuscate(); // Check if an attacker changed the honeypot fake value. if (this.fakeValue != (double)var_UnobfuscatedValue) { this.HasIntegrity = false; } // Return the integrity status. return this.HasIntegrity; } /// /// Disposes of the secured values array. /// public void Dispose() { this.obfuscatedValues = null; } /// /// Returns a string representation of the protected decimal's true value. /// /// The string representation of the true value. public override string ToString() { return this.Value.ToString(); } /// /// Gets the hash code of the protected decimal's true value. /// /// The hash code of the true value. public override int GetHashCode() { return this.Value.GetHashCode(); } #region Implicit operator /// /// Implicitly converts a decimal to a . /// /// The decimal value to be converted. /// A new instance of with the specified decimal value. public static implicit operator ProtectedDecimal(decimal _Value) { return new ProtectedDecimal(_Value); } /// /// Implicitly converts a to a decimal. /// /// The value to be converted. /// The decimal value of the specified . public static implicit operator decimal(ProtectedDecimal _Value) { return _Value.Value; } /// /// Implicitly converts a to a . /// /// The value to be converted. /// A new instance of with the converted value. public static implicit operator ProtectedDouble(ProtectedDecimal _Value) { return new ProtectedDouble((double)_Value.Value); } /// /// Implicitly converts a to a . /// /// The value to be converted. /// A new instance of with the converted value. public static implicit operator ProtectedDecimal(ProtectedDouble _Value) { return new ProtectedDecimal((decimal)_Value.Value); } /// /// Implicitly converts a to a . /// /// The value to be converted. /// A new instance of with the converted value. public static implicit operator ProtectedInt16(ProtectedDecimal _Value) { return new ProtectedInt16((Int16)_Value.Value); } /// /// Implicitly converts a to a . /// /// The value to be converted. /// A new instance of with the converted value. public static implicit operator ProtectedDecimal(ProtectedInt16 _Value) { return new ProtectedDecimal((decimal)_Value.Value); } /// /// Implicitly converts a to a . /// /// The value to be converted. /// A new instance of with the converted value. public static implicit operator ProtectedInt32(ProtectedDecimal _Value) { return new ProtectedInt32((Int32)_Value.Value); } /// /// Implicitly converts a to a . /// /// The value to be converted. /// A new instance of with the converted value. public static implicit operator ProtectedDecimal(ProtectedInt32 _Value) { return new ProtectedDecimal((decimal)_Value.Value); } /// /// Implicitly converts a to a . /// /// The value to be converted. /// A new instance of with the converted value. public static implicit operator ProtectedInt64(ProtectedDecimal _Value) { return new ProtectedInt64((Int64)_Value.Value); } /// /// Implicitly converts a to a . /// /// The value to be converted. /// A new instance of with the converted value. public static implicit operator ProtectedDecimal(ProtectedInt64 _Value) { return new ProtectedDecimal((decimal)_Value.Value); } /// /// Implicitly converts a to a . /// /// The value to be converted. /// A new instance of with the converted value. public static implicit operator ProtectedFloat(ProtectedDecimal _Value) { return new ProtectedFloat((float)_Value.Value); } /// /// Implicitly converts a to a . /// /// The value to be converted. /// A new instance of with the converted value. public static implicit operator ProtectedDecimal(ProtectedFloat _Value) { return new ProtectedDecimal((decimal)_Value.Value); } #endregion #region Equality operator /// /// Checks if two instances are equal. /// /// The first instance. /// The second instance. /// True if the values are equal; otherwise, false. public static bool operator ==(ProtectedDecimal v1, ProtectedDecimal v2) { return v1.Value == v2.Value; } /// /// Checks if two instances are not equal. /// /// The first instance. /// The second instance. /// True if the values are not equal; otherwise, false. public static bool operator !=(ProtectedDecimal v1, ProtectedDecimal v2) { return v1.Value != v2.Value; } /// /// Checks if the current is equal to the specified object. /// /// The object to compare with the current instance. /// True if the object is a and has the same value; otherwise, false. public override bool Equals(object obj) { if (obj is ProtectedDecimal) { return this.Value == ((ProtectedDecimal)obj).Value; } return this.Value.Equals(obj); } /// /// Checks if the value of the current is less than the value of another. /// /// The first instance. /// The second instance. /// True if the value of the first instance is less than the value of the second instance; otherwise, false. public static bool operator <(ProtectedDecimal v1, ProtectedDecimal v2) { return v1.Value < v2.Value; } /// /// Checks if the value of the current is less than or equal to the value of another. /// /// The first instance. /// The second instance. /// True if the value of the first instance is less than or equal to the value of the second instance; otherwise, false. public static bool operator <=(ProtectedDecimal v1, ProtectedDecimal v2) { return v1.Value <= v2.Value; } /// /// Checks if the value of the current is greater than the value of another. /// /// The first instance. /// The second instance. /// True if the value of the first instance is greater than the value of the second instance; otherwise, false. public static bool operator >(ProtectedDecimal v1, ProtectedDecimal v2) { return v1.Value > v2.Value; } /// /// Checks if the value of the current is greater than or equal to the value of another. /// /// The first instance. /// The second instance. /// True if the value of the first instance is greater than or equal to the value of the second instance; otherwise, false. public static bool operator >=(ProtectedDecimal v1, ProtectedDecimal v2) { return v1.Value >= v2.Value; } #endregion } }