View Single Post
Old 11-23-2011   #76
JuanNadie
Homebrew Developer
 
Join Date: Oct 2011
Posts: 20
Likes: 11
Liked 369 Times in 19 Posts
Mentioned: 68 Post(s)
Tagged: 0 Thread(s)
I haven´t fully reversed the EDAT algorithm (I’m missing bytes from 0xB0 to 0xFF) but there is enough info for a post. The explanation is for those with flag (offset 0x80-0x83) equals to 0xC which is a normal EDAT. For others values minor changes should be made. Let’s begin.

USER SPACE:
(Probably incomplete (enough for decryption), most of it is guessed)
On previous post I mentioned the command useds by developers to open/read an EDAT. That command has 3 parts:

-Verify: This parts verifies the NPD element by checking that user is authorized to open the file (using the devklic). In addition it calls LV2 to create an entry in the memory (probably using syscall471) so for further steps it will use the devklic (free) or the decrypted rif key (paid) (see SELF algorithm). I’ll call this rifkey on the rest of the document.
-Open the file: This will call kernel space. Performs several checks and creates an entry on another table.
-Read: It also calls LV2, which will decrypt and send data back.

<KERNEL SPACE:

-When an open request is received:
Read the first 0x100 bytes of the file.
Validates first 0xA0 bytes on EDAT. This uses appldr with the following execution (see below):
-Single execution
-Encrypted arguments
-CMAC. Key: rifkey, expectedHash: data at 0xA0-0xAF
-No Encryption erk:null,riv:null
Validates metadatasections. Again uses appldr:
-Multi execution. Blocks upto 0xFC00.
-Encrypted arguments
-CMAC. Key: rifkey, expectedHash:data at 0x90-0x9F
-No Encryption: erk:null, riv:null
Once the NPD is validated (We have validated header upto 0xAF and the metadatasections( which validate the data itself)) an entry on a memory table is created, the entry created by syscall471 is erased and returns. I’ll call this the EDATEncryptionDataTable. This table has 10 entries (remember the limitation of simultaneous files opened??...). Each entry has the following structure:

Code:
typedef struct {
    uint64_t fileDescriptor;
    uint64_t offset;
    uint64_t fileLen;
    uint32_t flag;
    uint32_t blockSize;
    uint32_tnumBlocks;
    uint8_t blockBase[0x0C];
    uint8_t devklic[0x10];
    uint8_t digest[0x10];	
}EDATEncryptionData ;
-fileDescriptor: The file descriptor
-offset: An additional offset to calculate the position of the metadataSection. Origin unknown. Values seen 0
-File len: Length of the uncompressed data. Copied from EDAT offset 0x88-0x8F
-flag: Flags required to process the data. Copied from EDAT offset 0x80-0x83. Some flags are:
0x80000000: Debug
0x01000000: SDAT… for those rifkey is calculated differently. Algorithm is not tested yet.
0x00000004: Arguments for appldr are encrypted
0x00000001: Data compressed. MetadataSection length will be 0x20 bytes.
-blockSize: The data is stored on blocks of that length. Required for decryption. Copied from EDAT offset 0x84-0x87
-numBlocks: Number of blocks on file. Calculated using: numBlocks = (fileLen + blockSize - 1)/blockSize
-blockBase: Common part of the key used on decryption. First 0xC bytes of headerHash. Copied from EDAT offset 0x60-0x6B.
-devklic: Our rif key. Copied from the entry created by syscall471. FOR SDAT it is calculated using other algorithm.
-padding: the digest from NPD. Will be the IV. Copied from EDAT offset 0x40-0x4F.


-When an read request is received:
- The system determines the blocks required (E.g; read from 0x3FFF to 0x8001 will read blocks 0,1 and 2).
-For each of the blocks:
- Calculate the blockKey as blockBase concat blockNumber.
- Calculate block offset: Start of NPD + offset + 0x100 + metadataSectionLen* numBlocks + blockSize*blockNumber. metadataSectionLen is 0x10 for flag 0xC (if compressed or with special hash would be 0x20).
-Calculate erk: AESECB128Encrypt, key:rifKey, data:blockKey
-Calculate hashKey: For 0xC is erk. (Others have an additional encryption)
- Execute appLdr with the following parameters:
-Single execution
-Encrypted arguments
-CMAC. Key: hashKey, expectedHash: metadataSection[blockIndex].hash
-EncryptionCBC erk:erk, riv:npd.digest
APPLDR:

As you know the appldr is used for decrypting SELFs (that part is documented on the wiki). However there is a second mode that is used for EDAT. To use that mode spu_arg.field35 is 5. For values 0 to 4 it will process a SELF and for others will return error. Graf_chokolo described the structure of spu_args for the SELF. However this changes for EDATs…

Code:
typedef struct
{
	uint64_t unk1;
	uint32_t hashFlags;
	uint32_t encryptionFlags;      
	uint64_t unk2;
	uint64_t len;
	uint64_t unk3;
	uint64_t stateAddress;       
	uint32_t field30;
	uint8_t  field34;       //Must be 0,1,2
	uint8_t  field35;       //Must be 0,1,2,3
	uint16_t  field36;      //Must be 0,1,2,3,4,5
	uint64_t field38;
	uint8_t hashKey[0x10];
	uint8_t riv[0x10];
	uint8_t erk[0x10];
}spu_args;
Above I indicated several parameters for the appldr. Those parameters are controlled by hashFlag and encryptionFlag.

-HashFlag: The hash is performed on input data not decrypted data. So if hash fails nothing is decrypted
MSB indicates how to use parameter hashKey.
0x10000000: Indicates that hashKey is encrypted. Decrypt it with EDATKEY and RIVKEY
0x20000000: Ignore hashKEY. USE EDATDEFAULTHASHKEY
0x00000000: hashKey is not encrypted. Use it directly
LSB indicates the type of hash.
0x00000001: HMACWithSHA1. Hash Len 0x14 bytes
0x00000002: CMAC128. Hash len 0x10 (obvious)
0x00000004: HMACWithSHA1. Hash Len 0x10 bytes (Hash is trunked)
-EncryptionFlag:
MSB indicates how to use parameters riv and erk
0x10000000: Indicated that erk is encrypted. Decrypt it with EDATKEY and RIVKEY. riv is copied
0x20000000: Ignore riv and erk. Use EDATKEY and RIVKEY
0x00000000: Use erk and riv directly
LSB indicates which encoding is used.
0x00000001: No encryption (algorithm is memcpy)
0x00000002: AESCBC128Decrypt is used.
When a key must be decrypted the algorithm is AESCBC128Decrypt. Keys SHA1:

EDATKEY: 84E9FC3574EAA11A9462FFA53D5EA46B4D0003BF. Already in wiki
RIVKEY: E129F27C5103BC5CC44BCDF0A15E160D445066FF. This is top secret ;D
EDATDEFAULTHASHKEY: 8A721A06ABC7BB9BF398C5EF5D6F1FD997BC0A56

The multiple execution /single execution refers to the ability of appLdr to store an encrypted (and hashed) data on main memory to resume the hashing/decryption operations on next execution. That data is stored at stateAddress. Basically the functioning has several submodes. One for initialization (that decrypts erk, riv and hkey) and copies the state to main memory, another for processing data, which retrieves the state, process the data and updates the state, and finally another that receives the expected hash and validates. Another submode does all these ops on a single execution (calls the three submodes).

Summing up (for 0xC) for each block:
- Calculate data offset: 0x100 + offset(0) + metadataSectionLen*numberOfBlocks + currentBlockNumber*blockSize
- Generate block key: concat first 0xC byte of headerHash with current block number
- Encrypt block key with rifKey using AESECB128Encrypt. This would be erk and hkey
- Calculate riv. It is NPD digest field
- Decrypt erk using AESCBC128Decrypt with EDATKEY and RIVKEY -> call result decryptionKey.
- Using AESCBC128Decrypt decrypt data from offset to offset + blockSize( if last block calculate remaining bytes rounded up to 0x10 multiple). Key is decryptionKey iv is riv
- Copy data to output file
JuanNadie is offline   Reply With Quote
Likes: (23)