So, after a couple of weeks of absense on my side, I'm finally back to reveal my findings on the intrinsics of the module chnnlsv.prx, which is reponsible for handling savedata encryption/decryption in the PSP.
Thanks to all the rest of the dev's team, Darth1701 and BlackDaemon's help, I was finally able to fully reverse engineer this module and unterstand it's crypto algorithms, which were still unknown until today.
The purpose of this post is to offer and disclose a brief pseudo code shaped summary of my findings before commiting anything to JPCSP/PCSP.
Please remember that this post is solely for investigational purposes!
-----------------------------------------------------------------------------------
[CHNNLSV.PRX]:
- Structs:
SDCtx1 {
int mode;
int unk;
byte[16] buf;
}
SDCtx2 {
int mode;
byte[16] result;
byte[16] key;
int keylength
}
- Functions' prototypes:
sceSdSetIndex(SDCtx2 ctx, int encMode);
sceSdCreateList(SDCtx1 ctx, int encMode, int genMode, byte[] data, byte[] key);
sceSdRemoveValue(SDCtx2 ctx, byte[] data, int length);
sceSdGetLastIndex(SDCtx2 ctx, byte[] hash, byte[] key);
sceSdSetMember(SDCtx1 ctx, byte[] data, int length);
sceChnnlsv_21BE78B4(SDCtx1 ctx);
- Details:
-> sceSdSetIndex: This function is responsible for initializing ctx's params to 0 and set ctx.mode to encMode.
-> sceSdCreateList: This is the main key generating function. The param genMode can be 1 for encryption or 2 for decryption. In the first case, a random pseudo number is encrypted and appended to decrypted data and it will be used as encrypting key after being XORed with the param key (if specified). In decryption, the first 16-bytes of data represent this generated hash, and all that happens is the XORing with the param key (if specified) and subsequent saving of this key for further use.
-> sceSdRemoveValue: This function is used in hash generation. This only matters in encryption processes, and it's used to collect and generate a key from the main encrypted data.
-> sceSdGetLastIndex: This function is also used in hash generation. It's called to generate a hash based on the context collected by sceSdRemoveValue. The hashes processed by this function must be written to the SAVEDATA_PARAMS field of the existing PARAM.SFO file when encrypting data to validate it.
-> sceSdSetMember: This is the main encryption/decryption function. It works by generating a binary list using a part of the key obtained by sceSdCreateList and a few custom algorithmic steps. Depending on which encMode is specified, different calls to KIRK CMDs can be used and different private CHNNLSV SD keys are XORed with the obtained key before generating the list (check the keys section). The final step consists on decrypting with KIRK CMD7 this binary list, and the result will then be XORed with the encrypted/decrypted data giving a decrypted/encrypted output.
-> sceChnnlsv_21BE78B4: Just clears the ctx struct. Pretty much like a sceSdSetIndex but for SDCtx1 instead.
- SD Keys (located in the chnnlsv.prx's subsection sceChnnlsv_driver):
Key3 and Key4 were the first encryption/decryption keys being used (up to firmware 2.5.2). Key6 and Key7 are the keys associated with the new mode being used since then.
Key2 and Key5 are used in hash generation and Key1 is still unknown.
-----------------------------------------------------------------------------------
Enjoy!
Thanks to all the rest of the dev's team, Darth1701 and BlackDaemon's help, I was finally able to fully reverse engineer this module and unterstand it's crypto algorithms, which were still unknown until today.
The purpose of this post is to offer and disclose a brief pseudo code shaped summary of my findings before commiting anything to JPCSP/PCSP.
Please remember that this post is solely for investigational purposes!
-----------------------------------------------------------------------------------
[CHNNLSV.PRX]:
- Structs:
SDCtx1 {
int mode;
int unk;
byte[16] buf;
}
SDCtx2 {
int mode;
byte[16] result;
byte[16] key;
int keylength
}
- Functions' prototypes:
sceSdSetIndex(SDCtx2 ctx, int encMode);
sceSdCreateList(SDCtx1 ctx, int encMode, int genMode, byte[] data, byte[] key);
sceSdRemoveValue(SDCtx2 ctx, byte[] data, int length);
sceSdGetLastIndex(SDCtx2 ctx, byte[] hash, byte[] key);
sceSdSetMember(SDCtx1 ctx, byte[] data, int length);
sceChnnlsv_21BE78B4(SDCtx1 ctx);
- Details:
-> sceSdSetIndex: This function is responsible for initializing ctx's params to 0 and set ctx.mode to encMode.
-> sceSdCreateList: This is the main key generating function. The param genMode can be 1 for encryption or 2 for decryption. In the first case, a random pseudo number is encrypted and appended to decrypted data and it will be used as encrypting key after being XORed with the param key (if specified). In decryption, the first 16-bytes of data represent this generated hash, and all that happens is the XORing with the param key (if specified) and subsequent saving of this key for further use.
-> sceSdRemoveValue: This function is used in hash generation. This only matters in encryption processes, and it's used to collect and generate a key from the main encrypted data.
-> sceSdGetLastIndex: This function is also used in hash generation. It's called to generate a hash based on the context collected by sceSdRemoveValue. The hashes processed by this function must be written to the SAVEDATA_PARAMS field of the existing PARAM.SFO file when encrypting data to validate it.
-> sceSdSetMember: This is the main encryption/decryption function. It works by generating a binary list using a part of the key obtained by sceSdCreateList and a few custom algorithmic steps. Depending on which encMode is specified, different calls to KIRK CMDs can be used and different private CHNNLSV SD keys are XORed with the obtained key before generating the list (check the keys section). The final step consists on decrypting with KIRK CMD7 this binary list, and the result will then be XORed with the encrypted/decrypted data giving a decrypted/encrypted output.
-> sceChnnlsv_21BE78B4: Just clears the ctx struct. Pretty much like a sceSdSetIndex but for SDCtx1 instead.
- SD Keys (located in the chnnlsv.prx's subsection sceChnnlsv_driver):
Code:
Key1 = {0x40, 0xE6, 0x53, 0x3F, 0x05, 0x11, 0x3A, 0x4E, 0xA1, 0x4B, 0xDA, 0xD6, 0x72, 0x7C, 0x53, 0x4C};
Key2 = {0xFA, 0xAA, 0x50, 0xEC, 0x2F, 0xDE, 0x54, 0x93, 0xAD, 0x14, 0xB2, 0xCE, 0xA5, 0x30, 0x05, 0xDF};
Key3 = {0x36, 0xA5, 0x3E, 0xAC, 0xC5, 0x26, 0x9E, 0xA3, 0x83, 0xD9, 0xEC, 0x25, 0x6C, 0x48, 0x48, 0x72};
Key4 = {0xD8, 0xC0, 0xB0, 0xF3, 0x3E, 0x6B, 0x76, 0x85, 0xFD, 0xFB, 0x4D, 0x7D, 0x45, 0x1E, 0x92, 0x03};
Key5 = {0xCB, 0x15, 0xF4, 0x07, 0xF9, 0x6A, 0x52, 0x3C, 0x04, 0xB9, 0xB2, 0xEE, 0x5C, 0x53, 0xFA, 0x86};
Key6 = {0x70, 0x44, 0xA3, 0xAE, 0xEF, 0x5D, 0xA5, 0xF2, 0x85, 0x7F, 0xF2, 0xD6, 0x94, 0xF5, 0x36, 0x3B};
Key7 = {0xEC, 0x6D, 0x29, 0x59, 0x26, 0x35, 0xA5, 0x7F, 0x97, 0x2A, 0x0D, 0xBC, 0xA3, 0x26, 0x33, 0x00};
Key3 and Key4 were the first encryption/decryption keys being used (up to firmware 2.5.2). Key6 and Key7 are the keys associated with the new mode being used since then.
Key2 and Key5 are used in hash generation and Key1 is still unknown.
-----------------------------------------------------------------------------------
Enjoy!