# Custom encryption

An **Encryption** implementation transforms serialized save data before writing and reverses that transformation when reading. Visual Script includes none, Caesar, and XOR implementations; a custom subclass adds another option to [Save settings](../../../manual/save-load/settings.md).

!!! tip "AES256 already provides a cipher"
    The free **[AES256](https://visualscript.dev/package/uq9Wsu9YwGglibbrTEUS)** module implements this interface. Install it when the project needs AES instead of maintaining another implementation.

## What this can and cannot do

!!! warning "Client-side encryption is obfuscation"
    The key ships inside your game, so a player with access to the game can extract it.

    Client-side encryption discourages casual edits, such as opening a save file and replacing `"gold": 100` with `"gold": 999999`. It doesn't make player-controlled data trustworthy.

    If save data affects a leaderboard, a shared economy, or purchases, validate it on a server.

Choose an implementation based on the edits you want to discourage and any format or platform requirements. A stronger local cipher doesn't replace server validation.

## The interface

Derive from `EncryptionBase` and implement a symmetric pair. This outline omits the encryption algorithm:

```cs
[Title("My Encryption")]
[Serializable]
public class EncryptionMyScheme : EncryptionBase
{
    [SerializeField] private string m_Key = "…";

    public override string Encrypt(string data) { /* … */ }
    public override string Decrypt(string data) { /* … */ }
}
```

`Decrypt(Encrypt(x))` must return exactly `x`, for every input the serializer can produce.

## Requirements

Check that your implementation preserves the serializer's output and fits the save operation:

| Requirement | Why |
| :--- | :--- |
| **Exact round trip** | A single altered character can make the whole save unparseable. |
| **Text-safe output** | The result is stored as a string. Binary output needs base64 encoding. |
| **Unicode-safe** | Player-entered names and localized text pass through it. |
| **Bounded runtime cost** | It runs on the whole save on the main thread, so its execution time contributes to the save pause. |

Account for key-derivation time when measuring that cost. Deliberately slow password key derivation can add a visible pause to a main-thread save.

## Handling failure

Return `null` when decryption receives data from another key, a truncated file, or corrupted input. This partial method shows the failure path; the decryption logic is omitted:

```cs
public override string Decrypt(string data)
{
    try
    {
        // …
    }
    catch
    {
        return null;
    }
}
```

Returning `null` lets the save system treat the data as an invalid save. An exception that escapes `Decrypt` aborts the load.

!!! warning "Changing the key makes existing saves unreadable"
    Saves written with the old key can't be read using the new key, so players can no longer load that progress.

    Choose the key before release. If you change it afterward, keep the old scheme available and fall back to it when the new one fails to decrypt.

## Testing

- Round-trip the largest realistic save.
- Round-trip text with quotes, newlines, and non-ASCII characters.
- Decrypt data encrypted with a different key, and confirm it fails cleanly.
- Decrypt truncated data, and confirm it fails cleanly.
- Decrypt plain unencrypted text, and confirm it fails cleanly.

The failure cases verify that invalid data does not prevent the game from launching.

## Choose encryption by threat

For a single-player game, the built-in XOR obscures plain text without adding another package. It does not protect data against a determined player.

Write a custom scheme for a specific requirement, such as a platform mandate or service interoperability. Define that requirement before choosing the algorithm so you can verify that the implementation meets it.

## Where to go next

- **[Custom serializers](serializers.md)** — control the text passed into encryption.
- **[Custom storage](storage.md)** — receive the encrypted result.
- **[Save settings](../../../manual/save-load/settings.md)** — select an encryption implementation.
