// 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 Int32, designed to enhance integrity and security by obfuscating its value and incorporating anti-cheat measures. /// In most cases, this protected Int32 can be used as a drop-in replacement for the default Int32 type. /// [Serializable] public struct ProtectedInt32 : 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 obfuscated value of the protected. /// private Int32 obfuscatedValue; /// /// A secret key used to obfuscate the true value. /// private Int32 secret; /// /// A honeypot pretending to be the original value. If some user tries to change this value via a cheat/hack engine, you will get notified. /// The protected value will keep its true value. /// [SerializeField] private Int32 fakeValue; /// /// Unity serialization hook. Ensures the correct values will be serialized. /// public void OnBeforeSerialize() { this.fakeValue = Value; } /// /// Unity deserialization hook. Ensures the correct values will be deserialized. /// public void OnAfterDeserialize() { this = this.fakeValue; } /// /// Creates a new protected Int32 with the specified initial value. /// /// The initial value of the protected Int32. public ProtectedInt32(Int32 _Value = 0) { // Initialization this.isInitialized = true; this.obfuscatedValue = 0; this.secret = GlobalSettings.RandomProvider.RandomInt32(1, Int32.MaxValue); this.fakeValue = 0; this.hasIntegrity = true; // Obfuscate the value. this.Obfuscate(_Value); } /// /// Gets and sets the true unencrypted field value. /// public Int32 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. /// private void Obfuscate(Int32 _Value) { // Obfuscate the value. this.obfuscatedValue = _Value ^ this.secret; // Assign the fake value. this.fakeValue = _Value; } /// /// Unobfuscates the secured value and returns the true unencrypted value. /// /// The true unencrypted value. private Int32 UnObfuscate() { // Get the unobfuscated value. return (Int32)(this.obfuscatedValue ^ this.secret); } /// /// Obfuscates the current value, generating a new random secret key. /// public void Obfuscate() { // Unobfuscate the secured value. Int32 var_UnobfuscatedValue = this.UnObfuscate(); // Create a new random secret. this.secret = GlobalSettings.RandomProvider.RandomInt32(1, Int32.MaxValue); // Obfuscate the value. this.Obfuscate(var_UnobfuscatedValue); } /// /// Checks the integrity of the protected value, detecting if an attacher changed the honeypot fake value. /// /// True if the protected value has integrity; otherwise, false. public bool CheckIntegrity() { // Unobfuscate the secured value. Int32 var_UnobfuscatedValue = this.UnObfuscate(); // Check if an attacher changed the honeypot fake value. if (this.fakeValue != var_UnobfuscatedValue) { this.HasIntegrity = false; } // Return the integrity status. return this.HasIntegrity; } /// /// Disposes of the secured and secret values. /// public void Dispose() { this.obfuscatedValue = 0; this.secret = 0; } /// /// Converts the protected Int32 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 Int32's true value. /// /// The hash code of the true value. public override int GetHashCode() { return (int)this.Value; } #region Serialization /// /// Used to serialize the protected to the player prefs. /// /// The obfuscated value of the protected. /// The secret key used to obfuscate the true value. internal void Serialize(out int _ObfuscatedValue, out int _Secret) { _ObfuscatedValue = this.obfuscatedValue; _Secret = this.secret; } /// /// Used to deserialize the protected from the player prefs. /// /// The obfuscated value of the protected. /// The secret key used to obfuscate the true value. internal void Deserialize(int _ObfuscatedValue, int _Secret) { this.obfuscatedValue = _ObfuscatedValue; this.secret = _Secret; this.fakeValue = this.UnObfuscate(); } #endregion #region Implicit operator /// /// Implicitly converts an Int32 value to a protected Int32. /// /// The Int32 value to convert. /// The corresponding protected Int32. public static implicit operator ProtectedInt32(Int32 _Value) { return new ProtectedInt32(_Value); } /// /// Implicitly converts a protected Int32 to its Int32 value. /// /// The protected Int32 to convert. /// The Int32 value of the protected Int32. public static implicit operator Int32(ProtectedInt32 _Value) { return _Value.Value; } #endregion #region Calculation operator /// /// Adds two protected Int32 values. /// /// The first protected Int32. /// The second protected Int32. /// The result of the addition. public static ProtectedInt32 operator +(ProtectedInt32 v1, ProtectedInt32 v2) { return new ProtectedInt32(v1.Value + v2.Value); } /// /// Subtracts the second protected Int32 from the first. /// /// The first protected Int32. /// The second protected Int32. /// The result of the subtraction. public static ProtectedInt32 operator -(ProtectedInt32 v1, ProtectedInt32 v2) { return new ProtectedInt32(v1.Value - v2.Value); } /// /// Multiplies two protected Int32 values. /// /// The first protected Int32. /// The second protected Int32. /// The result of the multiplication. public static ProtectedInt32 operator *(ProtectedInt32 v1, ProtectedInt32 v2) { return new ProtectedInt32(v1.Value * v2.Value); } /// /// Divides the first protected Int32 by the second. /// /// The first protected Int32. /// The second protected Int32. /// The result of the division. public static ProtectedInt32 operator /(ProtectedInt32 v1, ProtectedInt32 v2) { return new ProtectedInt32(v1.Value / v2.Value); } #endregion #region Equality operator /// /// Checks if two protected Int32 values are equal based on their true values. /// /// The first protected Int32. /// The second protected Int32. /// True if the true values are equal; otherwise, false. public static bool operator ==(ProtectedInt32 v1, ProtectedInt32 v2) { return v1.Value == v2.Value; } /// /// Checks if two protected Int32 values are not equal based on their true values. /// /// The first protected Int32. /// The second protected Int32. /// True if the true values are not equal; otherwise, false. public static bool operator !=(ProtectedInt32 v1, ProtectedInt32 v2) { return v1.Value != v2.Value; } /// /// Checks if the protected Int32 is equal to another object based on their true values. /// /// The object to compare with the protected Int32. /// True if the true values are equal; otherwise, false. public override bool Equals(object obj) { if (obj is ProtectedInt32) { return this.Value == ((ProtectedInt32)obj).Value; } return this.Value.Equals(obj); } /// /// Compares two protected Int32 values. /// /// The first protected Int32. /// The second protected Int32. /// True if the first value is less than the second; otherwise, false. public static bool operator <(ProtectedInt32 v1, ProtectedInt32 v2) { return v1.Value < v2.Value; } /// /// Checks if the first protected Int32 is less than or equal to the second. /// /// The first protected Int32. /// The second protected Int32. /// True if the first value is less than or equal to the second; otherwise, false. public static bool operator <=(ProtectedInt32 v1, ProtectedInt32 v2) { return v1.Value <= v2.Value; } /// /// Checks if the first protected Int32 is greater than the second. /// /// The first protected Int32. /// The second protected Int32. /// True if the first value is greater than the second; otherwise, false. public static bool operator >(ProtectedInt32 v1, ProtectedInt32 v2) { return v1.Value > v2.Value; } /// /// Checks if the first protected Int32 is greater than or equal to the second. /// /// The first protected Int32. /// The second protected Int32. /// True if the first value is greater than or equal to the second; otherwise, false. public static bool operator >=(ProtectedInt32 v1, ProtectedInt32 v2) { return v1.Value >= v2.Value; } #endregion } }