# Apollo Save File Encryption and Decryption: Algorithms and Key Locations

> Discover Apollo's save file encryption and decryption algorithms including RC4, AES-CBC/ECB, and SHA-1. Locate hard-coded keys within the source files of the bucanero/apollo-ps4 repository.

- Repository: [Damián Parrino/apollo-ps4](https://github.com/bucanero/apollo-ps4)
- Tags: internals
- Published: 2026-02-26

---

**Apollo uses RC4 for PS2 CodeBreaker saves, AES-CBC-128 for PS1 MCX memory cards, AES-ECB/CBC with SHA-1 for PSV/VMP resigning, and system-generated sealed keys for PS4 encrypted saves, with all hard-coded keys defined in specific source files under `source/`.**

The Apollo save game manager for PlayStation 4 (repository `bucanero/apollo-ps4`) implements multiple cryptographic primitives to handle legacy and modern save formats. Each algorithm is tailored to the specific console generation's security model, with keys either embedded in the source code or generated at runtime through kernel interfaces.

## RC4 Stream Cipher for PS2 CodeBreaker Saves (*.cbs)

### Key Location and Implementation

Apollo handles PS2 CodeBreaker saves using the **RC4 (ARC4)** stream cipher. The 256-byte permutation table is defined as a static constant named `cbsKey` in [`source/psv_ps2.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/psv_ps2.c) at lines 24-55. This hard-coded key table is identical for both encryption and decryption operations because RC4 is symmetric.

### Encryption and Decryption Process

The `cbsCrypt()` function initializes an `arc4_context` structure, copies the static `cbsKey` into the context, and performs in-place encryption or decryption using `arc4_crypt()`.

```c
/* psv_ps2.c – cbsCrypt() */
static void cbsCrypt(uint8_t *buf, size_t bufLen)
{
    arc4_context ctx;                       // ARC4 (RC4) context
    memset(&ctx, 0, sizeof(arc4_context));
    memcpy(ctx.m, cbsKey, sizeof(cbsKey));  // static 256-byte key table
    arc4_crypt(&ctx, bufLen, buf, buf);    // encrypt / decrypt in-place
}

```

Because RC4 generates a keystream that is XORed with plaintext, calling `cbsCrypt()` twice on the same buffer returns the original data, making the same function serve both operations.

## AES-CBC-128 for PS1 MCX Memory Cards

### Static Key and IV Definitions

For PS1 MCX (encrypted memory card) files, Apollo implements **AES-CBC-128**. The cryptographic material consists of a 16-byte key (`mcxKey`) and a 16-byte initialization vector (`mcxIv`), both defined as static constants in [`source/ps1card.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/ps1card.c) at lines 32-35.

### Implementation Details

The `AesCbcEncrypt()` function initializes an AES context, sets the 128-bit encryption key, and processes the buffer using CBC mode. The corresponding `AesCbcDecrypt()` function uses the same key and IV for reverse operation.

```c
/* ps1card.c – AesCbcEncrypt() */
static void AesCbcEncrypt(uint8_t *toEncrypt, size_t len,
                         const uint8_t *key, const uint8_t *aes_iv)
{
    uint8_t iv[16];
    aes_context ctx;

    memcpy(iv, aes_iv, 16);
    aes_init(&ctx);
    aes_setkey_enc(&ctx, key, 128);          // 128-bit key
    aes_crypt_cbc(&ctx, AES_ENCRYPT, len, iv, toEncrypt, toEncrypt);
    aes_free(&ctx);
}

```

The static nature of `mcxKey` and `mcxIv` means all MCX files processed by Apollo use the same encryption parameters, consistent with the legacy PS1 memory card format specification.

## AES and SHA-1 for PSV/VMP Re-signing

### Key Hierarchy and Hash Generation

Apollo re-signs PSV (PS3-style) and VMP (Vita) files using a combination of **AES-ECB**, **AES-CBC**, and **SHA-1**. The process uses three static values defined in [`source/psv_resign.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/psv_resign.c) at lines 39-49: `psv_ps1key` (16-byte), `psv_ps2key` (16-byte), and `psv_iv` (16-byte).

The `generateHash()` function creates a new signature by decrypting a salt with AES-ECB, re-encrypting with AES-ECB, XORing with the IV, then hashing the result with SHA-1.

```c
/* psv_resign.c – generateHash() */
aes_setkey_dec(&aes_ctx, psv_ps1key, 128);
aes_crypt_ecb(&aes_ctx, AES_DECRYPT, work_buf, salt);
aes_setkey_enc(&aes_ctx, psv_ps1key, 128);
aes_crypt_ecb(&aes_ctx, AES_ENCRYPT, work_buf, salt + 0x10);
XorWithIv(salt, psv_iv);
...
sha1_starts(&sha1_ctx);
sha1_update(&sha1_ctx, salt, sizeof(salt));
sha1_update(&sha1_ctx, input, sz);
sha1_finish(&sha1_ctx, work_buf);

```

The resulting 20-byte hash is written to `PSV_HASH_OFFSET` in the file header, replacing the original signature while preserving the save data integrity.

## Sealed Keys for PS4 Encrypted Saves

### Runtime Key Generation

For PS4 encrypted saves, Apollo does not perform cryptographic operations in userspace. Instead, it relies on the **Orbis kernel** to handle encryption via sealed keys. The `generateSealedKey()` function in [`source/sd.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/sd.c) interfaces with `/dev/sbl_srv` using ioctl `0x40845303` to request the kernel generate a 256-byte sealed key.

```c
/* sd.c – generateSealedKey() */
int generateSealedKey(uint8_t data[ENC_SEALEDKEY_LEN])
{
    uint8_t sealedKey[ENC_SEALEDKEY_LEN];
    int fd = open("/dev/sbl_srv", O_RDWR);
    ioctl(fd, 0x40845303, sealedKey);   // kernel creates the sealed key
    memcpy(data, sealedKey, sizeof(sealedKey));
    close(fd);
    return 0;
}

```

### Volume Key File Storage

The sealed key is persisted to a **volume key file** on the user's filesystem (conventionally named `save.bin.key`). The `createSave()` function in [`source/sd.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/sd.c) (lines 129-150) writes the sealed key to the path specified by `volumeKeyPath`, while `mountSave()` (lines 189-202) reads it back via `decryptSealedKeyAtPath()` and passes the decrypted key to `sceFsMountSaveData()`.

```c
/* sd.c – createSave() – writes the key to a file */
fd = sceKernelOpen(volumeKeyPath, O_CREAT|O_TRUNC|O_WRONLY, 0777);
sceKernelWrite(fd, sealedKey, sizeof(sealedKey));
sceKernelClose(fd);

```

This architecture ensures that the actual encryption keys never exist in plaintext within Apollo's memory or source code, leveraging the PS4's hardware security module for PS4 save operations.

## Summary

- **RC4 (ARC4)** encrypts PS2 CodeBreaker saves using the static `cbsKey` table defined in [`source/psv_ps2.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/psv_ps2.c).
- **AES-CBC-128** protects PS1 MCX memory cards with the static `mcxKey` and `mcxIv` constants in [`source/ps1card.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/ps1card.c).
- **AES-ECB/CBC with SHA-1** generates new signatures for PSV/VMP files using `psv_ps1key`, `psv_ps2key`, and `psv_iv` from [`source/psv_resign.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/psv_resign.c).
- **Sealed keys** handle PS4 save encryption via the kernel; Apollo generates these keys in [`source/sd.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/sd.c) and stores them in user-supplied volume key files.

## Frequently Asked Questions

### What encryption algorithm does Apollo use for PS2 saves?

Apollo uses the **RC4 (ARC4)** stream cipher for PS2 CodeBreaker (*.cbs) files. The algorithm is implemented in [`source/psv_ps2.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/psv_ps2.c) within the `cbsCrypt()` function, which uses a static 256-byte permutation table called `cbsKey` to encrypt or decrypt the save data in-place.

### Where are the AES keys stored for PS1 memory card operations?

The AES-128 keys for PS1 MCX files are stored as static constants in [`source/ps1card.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/ps1card.c) at lines 32-35. Specifically, `mcxKey` (16 bytes) and `mcxIv` (16 bytes) are hard-coded and used by the `AesCbcEncrypt()` and `AesCbcDecrypt()` functions to process encrypted PS1 memory card images.

### How does Apollo handle PS4 save file encryption?

Apollo does not perform PS4 encryption in userspace. Instead, it relies on the **Orbis kernel** and the `sbl_srv` device. The `generateSealedKey()` function in [`source/sd.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/sd.c) requests a sealed key from the kernel via ioctl `0x40845303`, stores it in a volume key file (e.g., `save.bin.key`), and later passes it to `sceFsMountSaveData()` through `mountSave()` to access encrypted saves.

### Can Apollo decrypt PSV files without the original keys?

Yes, Apollo can re-sign PSV files because it contains the necessary cryptographic material in [`source/psv_resign.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/psv_resign.c). The `generateHash()` function uses hard-coded keys (`psv_ps1key` for PS1-style saves, `psv_ps2key` for PS2-style saves) and the initialization vector `psv_iv` to generate a new SHA-1 hash and AES-based signature, effectively allowing the file to be resigned without requiring the original user's keys.