746 lines
21 KiB
C
746 lines
21 KiB
C
/* radare - LGPL - Copyright 2016 - pancake */
|
|
|
|
/* XXX this must be specified by the user/rom? */
|
|
#define UPPER_LIMIT 0xffffff
|
|
|
|
#define BIT(x,n) (((x)>>(n))&1)
|
|
|
|
#define BITSWAP8(val,B7,B6,B5,B4,B3,B2,B1,B0) \
|
|
((BIT(val,B7) << 7) | (BIT(val,B6) << 6) | (BIT(val,B5) << 5) | (BIT(val,B4) << 4) | \
|
|
(BIT(val,B3) << 3) | (BIT(val,B2) << 2) | (BIT(val,B1) << 1) | (BIT(val,B0) << 0))
|
|
|
|
// license:BSD-3-Clause
|
|
// copyright-holders:Paul Leaman, Andreas Naive, Nicola Salmoria,Charles MacDonald
|
|
/******************************************************************************
|
|
|
|
CPS-2 Encryption
|
|
|
|
All credit goes to Andreas Naive for breaking the encryption algorithm.
|
|
Code by Nicola Salmoria.
|
|
Thanks to Charles MacDonald and Razoola for extracting the data from the hardware.
|
|
|
|
|
|
The encryption only affects opcodes, not data.
|
|
|
|
It consists of two 4-round Feistel networks (FN) and involves both
|
|
the 16-bit opcode and the low 16 bits of the address.
|
|
|
|
Let be:
|
|
|
|
E = 16-bit ciphertext
|
|
A = 16-bit address
|
|
K = 64-bit key
|
|
D = 16-bit plaintext
|
|
y = FN1(x,k) = function describing the first Feistel network (x,y = 16 bit, k = 64 bit)
|
|
y = FN2(x,k) = function describing the second Feistel network (x,y = 16 bit, k = 64 bit)
|
|
y = EX(x) = fixed function that expands the 16-bit x to the 64-bit y
|
|
|
|
Then the cipher can be described as:
|
|
|
|
D = FN2( E, K XOR EX( FN1(A, K ) ) )
|
|
|
|
|
|
Each round of the Feistel networks consists of four substitution boxes. The boxes
|
|
have 6 inputs and 2 outputs. Usually the input is the XOR of a data bit and a key
|
|
bit, however in some cases only the key is used.
|
|
|
|
(TODO-notes about accuracy of s-boxes)
|
|
|
|
The s-boxes were chosen in order to use an empty key (all FF) for the dead board.
|
|
|
|
|
|
Also, the hardware has different watchdog opcodes and address range (see below)
|
|
which are stored in the battery backed RAM. There doesn't appear to be any relation
|
|
between those and the 64-bit encryption key, so they probably use an additional
|
|
64 bits of battery-backed RAM.
|
|
|
|
|
|
|
|
First FN:
|
|
|
|
B(0 1 3 5 8 9 11 12) A(10 4 6 7 2 13 15 14)
|
|
L0 R0
|
|
| |
|
|
XOR<-----------[F1]<------------|
|
|
| |
|
|
R1 L1
|
|
| |
|
|
|------------>[F2]----------->XOR
|
|
| |
|
|
L2 R2
|
|
| |
|
|
XOR<-----------[F3]<------------|
|
|
| |
|
|
R3 L3
|
|
| |
|
|
|------------>[F4]----------->XOR
|
|
| |
|
|
L4 R4
|
|
(10 4 6 7 2 13 15 14) (0 1 3 5 8 9 11 12)
|
|
|
|
|
|
Second FN:
|
|
|
|
B(3 5 9 10 8 15 12 11) A(6 0 2 13 1 4 14 7)
|
|
L0 R0
|
|
| |
|
|
XOR<-----------[F1]<------------|
|
|
| |
|
|
R1 L1
|
|
| |
|
|
|------------>[F2]----------->XOR
|
|
| |
|
|
L2 R2
|
|
| |
|
|
XOR<-----------[F3]<------------|
|
|
| |
|
|
R3 L3
|
|
| |
|
|
|------------>[F4]----------->XOR
|
|
| |
|
|
L4 R4
|
|
(6 0 2 13 1 4 14 7) (3 5 9 10 8 15 12 11)
|
|
|
|
******************************************************************************
|
|
|
|
Some Encryption notes.
|
|
----------------------
|
|
|
|
Address range.
|
|
|
|
The encryption does _not_ cover the entire address space. The range covered
|
|
differs per game.
|
|
|
|
|
|
Encryption Watchdog.
|
|
|
|
The CPS2 system has a watchdog system that will disable the decryption
|
|
of data if the watchdog isn't triggered at least once every few seconds.
|
|
The trigger varies from game to game (some games do use the same) and is
|
|
basically a 68000 opcode/s instruction. The instruction is the same for
|
|
all regions of the game. The watchdog instructions are listed alongside
|
|
the decryption keys.
|
|
|
|
*******************************************************************************/
|
|
|
|
#include <r_crypto.h>
|
|
|
|
/******************************************************************************/
|
|
|
|
static const int fn1_groupA[8] = { 10, 4, 6, 7, 2, 13, 15, 14 };
|
|
static const int fn1_groupB[8] = { 0, 1, 3, 5, 8, 9, 11, 12 };
|
|
|
|
static const int fn2_groupA[8] = { 6, 0, 2, 13, 1, 4, 14, 7 };
|
|
static const int fn2_groupB[8] = { 3, 5, 9, 10, 8, 15, 12, 11 };
|
|
|
|
/******************************************************************************/
|
|
|
|
// The order of the input and output bits in the s-boxes is arbitrary.
|
|
// Each s-box can be XORed with an arbitrary vale in range 0-3 (but the same value
|
|
// must be used for the corresponding output bits in f1 and f3 or in f2 and f4)
|
|
|
|
struct sbox {
|
|
const ut8 table[64];
|
|
const int inputs[6]; // positions of the inputs bits, -1 means no input except from key
|
|
const int outputs[2]; // positions of the output bits
|
|
};
|
|
|
|
// the above struct better defines how the hardware works, however
|
|
// to speed up the decryption at run time we convert it to the
|
|
// following one
|
|
struct optimised_sbox {
|
|
ut8 input_lookup[256];
|
|
ut8 output[64];
|
|
};
|
|
|
|
static const struct sbox fn1_r1_boxes[4] = {
|
|
{ // subkey bits 0- 5
|
|
{
|
|
0,2,2,0,1,0,1,1,3,2,0,3,0,3,1,2,1,1,1,2,1,3,2,2,2,3,3,2,1,1,1,2,
|
|
2,2,0,0,3,1,3,1,1,1,3,0,0,1,0,0,1,2,2,1,2,3,2,2,2,3,1,3,2,0,1,3,
|
|
},
|
|
{ 3, 4, 5, 6, -1, -1 },
|
|
{ 3, 6 }
|
|
},
|
|
{ // subkey bits 6-11
|
|
{
|
|
3,0,2,2,2,1,1,1,1,2,1,0,0,0,2,3,2,3,1,3,0,0,0,2,1,2,2,3,0,3,3,3,
|
|
0,1,3,2,3,3,3,1,1,1,1,2,0,1,2,1,3,2,3,1,1,3,2,2,2,3,1,3,2,3,0,0,
|
|
},
|
|
{ 0, 1, 2, 4, 7, -1 },
|
|
{ 2, 7 }
|
|
},
|
|
{ // subkey bits 12-17
|
|
{
|
|
3,0,3,1,1,0,2,2,3,1,2,0,3,3,2,3,0,1,0,1,2,3,0,2,0,2,0,1,0,0,1,0,
|
|
2,3,1,2,1,0,2,0,2,1,0,1,0,2,1,0,3,1,2,3,1,3,1,1,1,2,0,2,2,0,0,0,
|
|
},
|
|
{ 0, 1, 2, 3, 6, 7 },
|
|
{ 0, 1 }
|
|
},
|
|
{ // subkey bits 18-23
|
|
{
|
|
3,2,0,3,0,2,2,1,1,2,3,2,1,3,2,1,2,2,1,3,3,2,1,0,1,0,1,3,0,0,0,2,
|
|
2,1,0,1,0,1,0,1,3,1,1,2,2,3,2,0,3,3,2,0,2,1,3,3,0,0,3,0,1,1,3,3,
|
|
},
|
|
{ 0, 1, 3, 5, 6, 7 },
|
|
{ 4, 5 }
|
|
},
|
|
};
|
|
|
|
static const struct sbox fn1_r2_boxes[4] = {
|
|
{ // subkey bits 24-29
|
|
{
|
|
3,3,2,0,3,0,3,1,0,3,0,1,0,2,1,3,1,3,0,3,3,1,3,3,3,2,3,2,2,3,1,2,
|
|
0,2,2,1,0,1,2,0,3,3,0,1,3,2,1,2,3,0,1,3,0,1,2,2,1,2,1,2,0,1,3,0,
|
|
},
|
|
{ 0, 1, 2, 3, 6, -1 },
|
|
{ 1, 6 }
|
|
},
|
|
{ // subkey bits 30-35
|
|
{
|
|
1,2,3,2,1,3,0,1,1,0,2,0,0,2,3,2,3,3,0,1,2,2,1,0,1,0,1,2,3,2,1,3,
|
|
2,2,2,0,1,0,2,3,2,1,2,1,2,1,0,3,0,1,2,3,1,2,1,3,2,0,3,2,3,0,2,0,
|
|
},
|
|
{ 2, 4, 5, 6, 7, -1 },
|
|
{ 5, 7 }
|
|
},
|
|
{ // subkey bits 36-41
|
|
{
|
|
0,1,0,2,1,1,0,1,0,2,2,2,1,3,0,0,1,1,3,1,2,2,2,3,1,0,3,3,3,2,2,2,
|
|
1,1,3,0,3,1,3,0,1,3,3,2,1,1,0,0,1,2,2,2,1,1,1,2,2,0,0,3,2,3,1,3,
|
|
},
|
|
{ 1, 2, 3, 4, 5, 7 },
|
|
{ 0, 3 }
|
|
},
|
|
{ // subkey bits 42-47
|
|
{
|
|
2,1,0,3,3,3,2,0,1,2,1,1,1,0,3,1,1,3,3,0,1,2,1,0,0,0,3,0,3,0,3,0,
|
|
1,3,3,3,0,3,2,0,2,1,2,2,2,1,1,3,0,1,0,1,0,1,1,1,1,3,1,0,1,2,3,3,
|
|
},
|
|
{ 0, 1, 3, 4, 6, 7 },
|
|
{ 2, 4 }
|
|
},
|
|
};
|
|
|
|
static const struct sbox fn1_r3_boxes[4] = {
|
|
{ // subkey bits 48-53
|
|
{
|
|
0,0,0,3,3,1,1,0,2,0,2,0,0,0,3,2,0,1,2,3,2,2,1,0,3,0,0,0,0,0,2,3,
|
|
3,0,0,1,1,2,3,3,0,1,3,2,0,1,3,3,2,0,0,1,0,2,0,0,0,3,1,3,3,3,3,3,
|
|
},
|
|
{ 0, 1, 5, 6, 7, -1 },
|
|
{ 0, 5 }
|
|
},
|
|
{ // subkey bits 54-59
|
|
{
|
|
2,3,2,3,0,2,3,0,2,2,3,0,3,2,0,2,1,0,2,3,1,1,1,0,0,1,0,2,1,2,2,1,
|
|
3,0,2,1,2,3,3,0,3,2,3,1,0,2,1,0,1,2,2,3,0,2,1,3,1,3,0,2,1,1,1,3,
|
|
},
|
|
{ 2, 3, 4, 6, 7, -1 },
|
|
{ 6, 7 }
|
|
},
|
|
{ // subkey bits 60-65
|
|
{
|
|
3,0,2,1,1,3,1,2,2,1,2,2,2,0,0,1,2,3,1,0,2,0,0,2,3,1,2,0,0,0,3,0,
|
|
2,1,1,2,0,0,1,2,3,1,1,2,0,1,3,0,3,1,1,0,0,2,3,0,0,0,0,3,2,0,0,0,
|
|
},
|
|
{ 0, 2, 3, 4, 5, 6 },
|
|
{ 1, 4 }
|
|
},
|
|
{ // subkey bits 66-71
|
|
{
|
|
0,1,0,0,2,1,3,2,3,3,2,1,0,1,1,1,1,1,0,3,3,1,1,0,0,2,2,1,0,3,3,2,
|
|
1,3,3,0,3,0,2,1,1,2,3,2,2,2,1,0,0,3,3,3,2,2,3,1,0,2,3,0,3,1,1,0,
|
|
},
|
|
{ 0, 1, 2, 3, 5, 7 },
|
|
{ 2, 3 }
|
|
},
|
|
};
|
|
|
|
static const struct sbox fn1_r4_boxes[4] = {
|
|
{ // subkey bits 72-77
|
|
{
|
|
1,1,1,1,1,0,1,3,3,2,3,0,1,2,0,2,3,3,0,1,2,1,2,3,0,3,2,3,2,0,1,2,
|
|
0,1,0,3,2,1,3,2,3,1,2,3,2,0,1,2,2,0,0,0,2,1,3,0,3,1,3,0,1,3,3,0,
|
|
},
|
|
{ 1, 2, 3, 4, 5, 7 },
|
|
{ 0, 4 }
|
|
},
|
|
{ // subkey bits 78-83
|
|
{
|
|
3,0,0,0,0,1,0,2,3,3,1,3,0,3,1,2,2,2,3,1,0,0,2,0,1,0,2,2,3,3,0,0,
|
|
1,1,3,0,2,3,0,3,0,3,0,2,0,2,0,1,0,3,0,1,3,1,1,0,0,1,3,3,2,2,1,0,
|
|
},
|
|
{ 0, 1, 2, 3, 5, 6 },
|
|
{ 1, 3 }
|
|
},
|
|
{ // subkey bits 84-89
|
|
{
|
|
0,1,1,2,0,1,3,1,2,0,3,2,0,0,3,0,3,0,1,2,2,3,3,2,3,2,0,1,0,0,1,0,
|
|
3,0,2,3,0,2,2,2,1,1,0,2,2,0,0,1,2,1,1,1,2,3,0,3,1,2,3,3,1,1,3,0,
|
|
},
|
|
{ 0, 2, 4, 5, 6, 7 },
|
|
{ 2, 6 }
|
|
},
|
|
{ // subkey bits 90-95
|
|
{
|
|
0,1,2,2,0,1,0,3,2,2,1,1,3,2,0,2,0,1,3,3,0,2,2,3,3,2,0,0,2,1,3,3,
|
|
1,1,1,3,1,2,1,1,0,3,3,2,3,2,3,0,3,1,0,0,3,0,0,0,2,2,2,1,2,3,0,0,
|
|
},
|
|
{ 0, 1, 3, 4, 6, 7 },
|
|
{ 5, 7 }
|
|
},
|
|
};
|
|
|
|
/******************************************************************************/
|
|
|
|
static const struct sbox fn2_r1_boxes[4] = {
|
|
{ // subkey bits 0- 5
|
|
{
|
|
2,0,2,0,3,0,0,3,1,1,0,1,3,2,0,1,2,0,1,2,0,2,0,2,2,2,3,0,2,1,3,0,
|
|
0,1,0,1,2,2,3,3,0,3,0,2,3,0,1,2,1,1,0,2,0,3,1,1,2,2,1,3,1,1,3,1,
|
|
},
|
|
{ 0, 3, 4, 5, 7, -1 },
|
|
{ 6, 7 }
|
|
},
|
|
{ // subkey bits 6-11
|
|
{
|
|
1,1,0,3,0,2,0,1,3,0,2,0,1,1,0,0,1,3,2,2,0,2,2,2,2,0,1,3,3,3,1,1,
|
|
1,3,1,3,2,2,2,2,2,2,0,1,0,1,1,2,3,1,1,2,0,3,3,3,2,2,3,1,1,1,3,0,
|
|
},
|
|
{ 1, 2, 3, 4, 6, -1 },
|
|
{ 3, 5 }
|
|
},
|
|
{ // subkey bits 12-17
|
|
{
|
|
1,0,2,2,3,3,3,3,1,2,2,1,0,1,2,1,1,2,3,1,2,0,0,1,2,3,1,2,0,0,0,2,
|
|
2,0,1,1,0,0,2,0,0,0,2,3,2,3,0,1,3,0,0,0,2,3,2,0,1,3,2,1,3,1,1,3,
|
|
},
|
|
{ 1, 2, 4, 5, 6, 7 },
|
|
{ 1, 4 }
|
|
},
|
|
{ // subkey bits 18-23
|
|
{
|
|
1,3,3,0,3,2,3,1,3,2,1,1,3,3,2,1,2,3,0,3,1,0,0,2,3,0,0,0,3,3,0,1,
|
|
2,3,0,0,0,1,2,1,3,0,0,1,0,2,2,2,3,3,1,2,1,3,0,0,0,3,0,1,3,2,2,0,
|
|
},
|
|
{ 0, 2, 3, 5, 6, 7 },
|
|
{ 0, 2 }
|
|
},
|
|
};
|
|
|
|
static const struct sbox fn2_r2_boxes[4] = {
|
|
{ // subkey bits 24-29
|
|
{
|
|
3,1,3,0,3,0,3,1,3,0,0,1,1,3,0,3,1,1,0,1,2,3,2,3,3,1,2,2,2,0,2,3,
|
|
2,2,2,1,1,3,3,0,3,1,2,1,1,1,0,2,0,3,3,0,0,2,0,0,1,1,2,1,2,1,1,0,
|
|
},
|
|
{ 0, 2, 4, 6, -1, -1 },
|
|
{ 4, 6 }
|
|
},
|
|
{ // subkey bits 30-35
|
|
{
|
|
0,3,0,3,3,2,1,2,3,1,1,1,2,0,2,3,0,3,1,2,2,1,3,3,3,2,1,2,2,0,1,0,
|
|
2,3,0,1,2,0,1,1,2,0,2,1,2,0,2,3,3,1,0,2,3,3,0,3,1,1,3,0,0,1,2,0,
|
|
},
|
|
{ 1, 3, 4, 5, 6, 7 },
|
|
{ 0, 3 }
|
|
},
|
|
{ // subkey bits 36-41
|
|
{
|
|
0,0,2,1,3,2,1,0,1,2,2,2,1,1,0,3,1,2,2,3,2,1,1,0,3,0,0,1,1,2,3,1,
|
|
3,3,2,2,1,0,1,1,1,2,0,1,2,3,0,3,3,0,3,2,2,0,2,2,1,2,3,2,1,0,2,1,
|
|
},
|
|
{ 0, 1, 3, 4, 5, 7 },
|
|
{ 1, 7 }
|
|
},
|
|
{ // subkey bits 42-47
|
|
{
|
|
0,2,1,2,0,2,2,0,1,3,2,0,3,2,3,0,3,3,2,3,1,2,3,1,2,2,0,0,2,2,1,2,
|
|
2,3,3,3,1,1,0,0,0,3,2,0,3,2,3,1,1,1,1,0,1,0,1,3,0,0,1,2,2,3,2,0,
|
|
},
|
|
{ 1, 2, 3, 5, 6, 7 },
|
|
{ 2, 5 }
|
|
},
|
|
};
|
|
|
|
static const struct sbox fn2_r3_boxes[4] = {
|
|
{ // subkey bits 48-53
|
|
{
|
|
2,1,2,1,2,3,1,3,2,2,1,3,3,0,0,1,0,2,0,3,3,1,0,0,1,1,0,2,3,2,1,2,
|
|
1,1,2,1,1,3,2,2,0,2,2,3,3,3,2,0,0,0,0,0,3,3,3,0,1,2,1,0,2,3,3,1,
|
|
},
|
|
{ 2, 3, 4, 6, -1, -1 },
|
|
{ 3, 5 }
|
|
},
|
|
{ // subkey bits 54-59
|
|
{
|
|
3,2,3,3,1,0,3,0,2,0,1,1,1,0,3,0,3,1,3,1,0,1,2,3,2,2,3,2,0,1,1,2,
|
|
3,0,0,2,1,0,0,2,2,0,1,0,0,2,0,0,1,3,1,3,2,0,3,3,1,0,2,2,2,3,0,0,
|
|
},
|
|
{ 0, 1, 3, 5, 7, -1 },
|
|
{ 0, 2 }
|
|
},
|
|
{ // subkey bits 60-65
|
|
{
|
|
2,2,1,0,2,3,3,0,0,0,1,3,1,2,3,2,2,3,1,3,0,3,0,3,3,2,2,1,0,0,0,2,
|
|
1,2,2,2,0,0,1,2,0,1,3,0,2,3,2,1,3,2,2,2,3,1,3,0,2,0,2,1,0,3,3,1,
|
|
},
|
|
{ 0, 1, 2, 3, 5, 7 },
|
|
{ 1, 6 }
|
|
},
|
|
{ // subkey bits 66-71
|
|
{
|
|
1,2,3,2,0,2,1,3,3,1,0,1,1,2,2,0,0,1,1,1,2,1,1,2,0,1,3,3,1,1,1,2,
|
|
3,3,1,0,2,1,1,1,2,1,0,0,2,2,3,2,3,2,2,0,2,2,3,3,0,2,3,0,2,2,1,1,
|
|
},
|
|
{ 0, 2, 4, 5, 6, 7 },
|
|
{ 4, 7 }
|
|
},
|
|
};
|
|
|
|
static const struct sbox fn2_r4_boxes[4] = {
|
|
{ // subkey bits 72-77
|
|
{
|
|
2,0,1,1,2,1,3,3,1,1,1,2,0,1,0,2,0,1,2,0,2,3,0,2,3,3,2,2,3,2,0,1,
|
|
3,0,2,0,2,3,1,3,2,0,0,1,1,2,3,1,1,1,0,1,2,0,3,3,1,1,1,3,3,1,1,0,
|
|
},
|
|
{ 0, 1, 3, 6, 7, -1 },
|
|
{ 0, 3 }
|
|
},
|
|
{ // subkey bits 78-83
|
|
{
|
|
1,2,2,1,0,3,3,1,0,2,2,2,1,0,1,0,1,1,0,1,0,2,1,0,2,1,0,2,3,2,3,3,
|
|
2,2,1,2,2,3,1,3,3,3,0,1,0,1,3,0,0,0,1,2,0,3,3,2,3,2,1,3,2,1,0,2,
|
|
},
|
|
{ 0, 1, 2, 4, 5, 6 },
|
|
{ 4, 7 }
|
|
},
|
|
{ // subkey bits 84-89
|
|
{
|
|
2,3,2,1,3,2,3,0,0,2,1,1,0,0,3,2,3,1,0,1,2,2,2,1,3,2,2,1,0,2,1,2,
|
|
0,3,1,0,0,3,1,1,3,3,2,0,1,0,1,3,0,0,1,2,1,2,3,2,1,0,0,3,2,1,1,3,
|
|
},
|
|
{ 0, 2, 3, 4, 5, 7 },
|
|
{ 1, 2 }
|
|
},
|
|
{ // subkey bits 90-95
|
|
{
|
|
2,0,0,3,2,2,2,1,3,3,1,1,2,0,0,3,1,0,3,2,1,0,2,0,3,2,2,3,2,0,3,0,
|
|
1,3,0,2,2,1,3,3,0,1,0,3,1,1,3,2,0,3,0,2,3,2,1,3,2,3,0,0,1,3,2,1,
|
|
},
|
|
{ 2, 3, 4, 5, 6, 7 },
|
|
{ 5, 6 }
|
|
},
|
|
};
|
|
|
|
/******************************************************************************/
|
|
|
|
static ut8 fn(ut8 in, const struct optimised_sbox *sboxes, ut32 key) {
|
|
const struct optimised_sbox *sbox1 = &sboxes[0];
|
|
const struct optimised_sbox *sbox2 = &sboxes[1];
|
|
const struct optimised_sbox *sbox3 = &sboxes[2];
|
|
const struct optimised_sbox *sbox4 = &sboxes[3];
|
|
|
|
return
|
|
sbox1->output[sbox1->input_lookup[in] ^ ((key >> 0) & 0x3f)] |
|
|
sbox2->output[sbox2->input_lookup[in] ^ ((key >> 6) & 0x3f)] |
|
|
sbox3->output[sbox3->input_lookup[in] ^ ((key >> 12) & 0x3f)] |
|
|
sbox4->output[sbox4->input_lookup[in] ^ ((key >> 18) & 0x3f)];
|
|
}
|
|
|
|
// srckey is the 64-bit master key (2x32 bits)
|
|
// dstkey will contain the 96-bit key for the 1st FN (4x24 bits)
|
|
static void expand_1st_key(ut32 *dstkey, const ut32 *srckey) {
|
|
static const int bits[96] = {
|
|
33, 58, 49, 36, 0, 31,
|
|
22, 30, 3, 16, 5, 53,
|
|
10, 41, 23, 19, 27, 39,
|
|
43, 6, 34, 12, 61, 21,
|
|
48, 13, 32, 35, 6, 42,
|
|
43, 14, 21, 41, 52, 25,
|
|
18, 47, 46, 37, 57, 53,
|
|
20, 8, 55, 54, 59, 60,
|
|
27, 33, 35, 18, 8, 15,
|
|
63, 1, 50, 44, 16, 46,
|
|
5, 4, 45, 51, 38, 25,
|
|
13, 11, 62, 29, 48, 2,
|
|
59, 61, 62, 56, 51, 57,
|
|
54, 9, 24, 63, 22, 7,
|
|
26, 42, 45, 40, 23, 14,
|
|
2, 31, 52, 28, 44, 17,
|
|
};
|
|
int i;
|
|
|
|
dstkey[0] = 0;
|
|
dstkey[1] = 0;
|
|
dstkey[2] = 0;
|
|
dstkey[3] = 0;
|
|
|
|
for (i = 0; i < 96; ++i)
|
|
dstkey[i / 24] |= BIT(srckey[bits[i] / 32], bits[i] % 32) << (i % 24);
|
|
}
|
|
|
|
// srckey is the 64-bit master key (2x32 bits) XORed with the subkey
|
|
// dstkey will contain the 96-bit key for the 2nd FN (4x24 bits)
|
|
static void expand_2nd_key(ut32 *dstkey, const ut32 *srckey) {
|
|
static const int bits[96] = {
|
|
34, 9, 32, 24, 44, 54,
|
|
38, 61, 47, 13, 28, 7,
|
|
29, 58, 18, 1, 20, 60,
|
|
15, 6, 11, 43, 39, 19,
|
|
63, 23, 16, 62, 54, 40,
|
|
31, 3, 56, 61, 17, 25,
|
|
47, 38, 55, 57, 5, 4,
|
|
15, 42, 22, 7, 2, 19,
|
|
46, 37, 29, 39, 12, 30,
|
|
49, 57, 31, 41, 26, 27,
|
|
24, 36, 11, 63, 33, 16,
|
|
56, 62, 48, 60, 59, 32,
|
|
12, 30, 53, 48, 10, 0,
|
|
50, 35, 3, 59, 14, 49,
|
|
51, 45, 44, 2, 21, 33,
|
|
55, 52, 23, 28, 8, 26,
|
|
};
|
|
int i;
|
|
|
|
dstkey[0] = 0;
|
|
dstkey[1] = 0;
|
|
dstkey[2] = 0;
|
|
dstkey[3] = 0;
|
|
|
|
for (i = 0; i < 96; ++i) {
|
|
dstkey[i / 24] |= BIT(srckey[bits[i] / 32], bits[i] % 32) << (i % 24);
|
|
}
|
|
}
|
|
|
|
// seed is the 16-bit seed generated by the first FN
|
|
// subkey will contain the 64-bit key to be XORed with the master key
|
|
// for the 2nd FN (2x32 bits)
|
|
static void expand_subkey(ut32* subkey, ut16 seed) {
|
|
// Note that each row of the table is a permutation of the seed bits.
|
|
static const int bits[64] = {
|
|
5, 10, 14, 9, 4, 0, 15, 6, 1, 8, 3, 2, 12, 7, 13, 11,
|
|
5, 12, 7, 2, 13, 11, 9, 14, 4, 1, 6, 10, 8, 0, 15, 3,
|
|
4, 10, 2, 0, 6, 9, 12, 1, 11, 7, 15, 8, 13, 5, 14, 3,
|
|
14, 11, 12, 7, 4, 5, 2, 10, 1, 15, 0, 9, 8, 6, 13, 3,
|
|
};
|
|
int i;
|
|
|
|
subkey[0] = 0;
|
|
subkey[1] = 0;
|
|
|
|
for (i = 0; i < 64; ++i) {
|
|
subkey[i / 32] |= BIT(seed, bits[i]) << (i % 32);
|
|
}
|
|
}
|
|
|
|
static inline ut16 feistel(ut16 val, const int *bitsA, const int *bitsB,
|
|
const struct optimised_sbox* boxes1, const struct optimised_sbox* boxes2,
|
|
const struct optimised_sbox* boxes3, const struct optimised_sbox* boxes4,
|
|
ut32 key1, ut32 key2, ut32 key3, ut32 key4)
|
|
{
|
|
ut8 l = BITSWAP8(val, bitsB[7],bitsB[6],bitsB[5],bitsB[4],bitsB[3],bitsB[2],bitsB[1],bitsB[0]);
|
|
ut8 r = BITSWAP8(val, bitsA[7],bitsA[6],bitsA[5],bitsA[4],bitsA[3],bitsA[2],bitsA[1],bitsA[0]);
|
|
|
|
l ^= fn(r, boxes1, key1);
|
|
r ^= fn(l, boxes2, key2);
|
|
l ^= fn(r, boxes3, key3);
|
|
r ^= fn(l, boxes4, key4);
|
|
|
|
return
|
|
(BIT(l, 0) << bitsA[0]) |
|
|
(BIT(l, 1) << bitsA[1]) |
|
|
(BIT(l, 2) << bitsA[2]) |
|
|
(BIT(l, 3) << bitsA[3]) |
|
|
(BIT(l, 4) << bitsA[4]) |
|
|
(BIT(l, 5) << bitsA[5]) |
|
|
(BIT(l, 6) << bitsA[6]) |
|
|
(BIT(l, 7) << bitsA[7]) |
|
|
(BIT(r, 0) << bitsB[0]) |
|
|
(BIT(r, 1) << bitsB[1]) |
|
|
(BIT(r, 2) << bitsB[2]) |
|
|
(BIT(r, 3) << bitsB[3]) |
|
|
(BIT(r, 4) << bitsB[4]) |
|
|
(BIT(r, 5) << bitsB[5]) |
|
|
(BIT(r, 6) << bitsB[6]) |
|
|
(BIT(r, 7) << bitsB[7]);
|
|
}
|
|
|
|
static int extract_inputs(ut32 val, const int *inputs) {
|
|
int i, res = 0;
|
|
for (i = 0; i < 6; ++i) {
|
|
if (inputs[i] != -1) {
|
|
res |= BIT (val, inputs[i]) << i;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static void optimise_sboxes(struct optimised_sbox* out, const struct sbox* in) {
|
|
int i, box;
|
|
|
|
for (box = 0; box < 4; ++box) {
|
|
// precalculate the input lookup
|
|
for (i = 0; i < 256; ++i) {
|
|
out[box].input_lookup[i] = extract_inputs(i, in[box].inputs);
|
|
}
|
|
// precalculate the output masks
|
|
for (i = 0; i < 64; ++i) {
|
|
int o = in[box].table[i];
|
|
out[box].output[i] = 0;
|
|
if (o & 1) {
|
|
out[box].output[i] |= 1 << in[box].outputs[0];
|
|
}
|
|
if (o & 2) {
|
|
out[box].output[i] |= 1 << in[box].outputs[1];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void cps2_decrypt(const ut16 *rom, ut16 *dec, int length, const ut32 *master_key, ut32 upper_limit) {
|
|
int i;
|
|
ut32 key1[4];
|
|
struct optimised_sbox sboxes1[4*4];
|
|
struct optimised_sbox sboxes2[4*4];
|
|
|
|
optimise_sboxes(&sboxes1[0*4], fn1_r1_boxes);
|
|
optimise_sboxes(&sboxes1[1*4], fn1_r2_boxes);
|
|
optimise_sboxes(&sboxes1[2*4], fn1_r3_boxes);
|
|
optimise_sboxes(&sboxes1[3*4], fn1_r4_boxes);
|
|
optimise_sboxes(&sboxes2[0*4], fn2_r1_boxes);
|
|
optimise_sboxes(&sboxes2[1*4], fn2_r2_boxes);
|
|
optimise_sboxes(&sboxes2[2*4], fn2_r3_boxes);
|
|
optimise_sboxes(&sboxes2[3*4], fn2_r4_boxes);
|
|
|
|
|
|
// expand master key to 1st FN 96-bit key
|
|
expand_1st_key(key1, master_key);
|
|
|
|
// add extra bits for s-boxes with less than 6 inputs
|
|
key1[0] ^= BIT(key1[0], 1) << 4;
|
|
key1[0] ^= BIT(key1[0], 2) << 5;
|
|
key1[0] ^= BIT(key1[0], 8) << 11;
|
|
key1[1] ^= BIT(key1[1], 0) << 5;
|
|
key1[1] ^= BIT(key1[1], 8) << 11;
|
|
key1[2] ^= BIT(key1[2], 1) << 5;
|
|
key1[2] ^= BIT(key1[2], 8) << 11;
|
|
|
|
for (i = 0; i < 0x10000; ++i) {
|
|
int a;
|
|
ut16 seed;
|
|
ut32 subkey[2];
|
|
ut32 key2[4];
|
|
|
|
if ((i & 0xff) == 0) {
|
|
eprintf ("Decrypting %d%%\r", i*100/0x10000);
|
|
}
|
|
|
|
// pass the address through FN1
|
|
seed = feistel(i, fn1_groupA, fn1_groupB,
|
|
&sboxes1[0*4], &sboxes1[1*4], &sboxes1[2*4], &sboxes1[3*4],
|
|
key1[0], key1[1], key1[2], key1[3]);
|
|
|
|
// expand the result to 64-bit
|
|
expand_subkey (subkey, seed);
|
|
|
|
// XOR with the master key
|
|
subkey[0] ^= master_key[0];
|
|
subkey[1] ^= master_key[1];
|
|
|
|
// expand key to 2nd FN 96-bit key
|
|
expand_2nd_key (key2, subkey);
|
|
|
|
// add extra bits for s-boxes with less than 6 inputs
|
|
key2[0] ^= BIT(key2[0], 0) << 5;
|
|
key2[0] ^= BIT(key2[0], 6) << 11;
|
|
key2[1] ^= BIT(key2[1], 0) << 5;
|
|
key2[1] ^= BIT(key2[1], 1) << 4;
|
|
key2[2] ^= BIT(key2[2], 2) << 5;
|
|
key2[2] ^= BIT(key2[2], 3) << 4;
|
|
key2[2] ^= BIT(key2[2], 7) << 11;
|
|
key2[3] ^= BIT(key2[3], 1) << 5;
|
|
|
|
// decrypt the opcodes
|
|
for (a = i; a < length/2 && a < upper_limit/2; a += 0x10000) {
|
|
dec[a] = feistel(rom[a], fn2_groupA, fn2_groupB,
|
|
&sboxes2[0*4], &sboxes2[1*4], &sboxes2[2*4], &sboxes2[3*4],
|
|
key2[0], key2[1], key2[2], key2[3]);
|
|
}
|
|
// copy the unencrypted part
|
|
while (a < length/2) {
|
|
dec[a] = rom[a];
|
|
a += 0x10000;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
main(cps_state,cps2crypt) {
|
|
ut32 key[2];
|
|
ut32 lower;
|
|
ut32 upper;
|
|
|
|
std::string skey1 = parameter("cryptkey1");;
|
|
key[0] = strtoll(skey1.c_str(), nullptr, 16);
|
|
|
|
std::string skey2 = parameter("cryptkey2");
|
|
key[1] = strtoll(skey2.c_str(), nullptr, 16);
|
|
|
|
std::string slower = parameter("cryptlower");
|
|
lower = strtoll(slower.c_str(), nullptr, 16); // unused
|
|
|
|
std::string supper = parameter("cryptupper");
|
|
upper = strtoll(supper.c_str(), nullptr, 16);
|
|
|
|
// we have a proper key so use it to decrypt
|
|
if (lower != 0xff0000) {// don't run the decrypt on 'dead key' games for now
|
|
cps2_decrypt( (ut16 *)memregion("maincpu")->base(), m_decrypted_opcodes, memregion("maincpu")->bytes(), key, lower,upper);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static ut32 cps2key[2] = {0};
|
|
|
|
static int set_key(RCrypto *cry, const ut8 *key, int keylen, int mode, int direction) {
|
|
if (keylen == 8) {
|
|
memcpy (&cps2key, key, keylen);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int get_key_size(RCrypto *cry) {
|
|
/* 64bit key */
|
|
return 8;
|
|
}
|
|
|
|
static bool cps2_use(const char *algo) {
|
|
return !strcmp (algo, "cps2");
|
|
}
|
|
|
|
static int update(RCrypto *cry, const ut8 *buf, int len) {
|
|
ut8 *output = calloc (1, len);
|
|
/* TODO : control decryption errors */
|
|
cps2_decrypt ((const ut16 *)buf, (ut16*)output, len, cps2key, UPPER_LIMIT);
|
|
r_crypto_append (cry, output, len);
|
|
free (output);
|
|
return true;
|
|
}
|
|
|
|
RCryptoPlugin r_crypto_plugin_cps2 = {
|
|
.name = "cps2",
|
|
.set_key = set_key,
|
|
.get_key_size = get_key_size,
|
|
.use = cps2_use,
|
|
.update = update
|
|
};
|
|
|
|
#ifndef CORELIB
|
|
struct r_lib_struct_t radare_plugin = {
|
|
.type = R_LIB_TYPE_CRYPTO,
|
|
.data = &r_crypto_plugin_rol,
|
|
.version = R2_VERSION
|
|
};
|
|
#endif
|