1 /*
2 * PSP Software Development Kit - http://www.pspdev.org
3 * -----------------------------------------------------------------------
4 * Licensed under the BSD license, see LICENSE in PSPSDK root for details.
5 *
6 * encrypt.c - Encryption routines using sceChnnlsv
7 *
8 * Copyright (c) 2005 Jim Paris <jim@jtan.com>
9 * Coypright (c) 2005 psp123
10 *
11 * $Id: encrypt.c 1560 2005-12-10 01:16:32Z jim $
12 */
13
14 #include "encrypt.h"
15 #include "hash.h"
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <malloc.h>
20 #include <pspchnnlsv.h>
21 #include "kernelcall/kernelcall.h"
22
align16(unsigned int v)23 static inline int align16(unsigned int v)
24 {
25 return ((v + 0xF) >> 4) << 4;
26 }
27
fopen_getsize(const char * filename,FILE ** fd,int * size)28 int fopen_getsize(const char *filename, FILE **fd, int *size)
29 {
30 if ((*fd = fopen(filename, "r")) == NULL)
31 return -1;
32
33 fseek(*fd, 0, SEEK_END);
34 *size = ftell(*fd);
35 fseek(*fd, 0, SEEK_SET);
36
37 if (*size <= 0) {
38 fclose(*fd);
39 return -2;
40 }
41
42 return 0;
43 }
44
45 /* Encrypt the given plaintext file, and update the message
46 authentication hashes in the param.sfo. The data_filename is
47 usually the final component of encrypted_filename, e.g. "DATA.BIN".
48 See main.c for an example of usage. */
encrypt_file(const char * plaintext_filename,const char * encrypted_filename,const char * data_filename,const char * paramsfo_filename,const char * paramsfo_filename_out,const unsigned char * gamekey,const int mainSdkVersion)49 int encrypt_file(const char *plaintext_filename,
50 const char *encrypted_filename,
51 const char *data_filename,
52 const char *paramsfo_filename,
53 const char *paramsfo_filename_out,
54 const unsigned char *gamekey,
55 const int mainSdkVersion)
56 {
57 FILE *in = NULL, *out = NULL, *sfo = NULL;
58 unsigned char *data = NULL, *cryptkey = NULL, *hash = NULL;
59 unsigned char paramsfo[0x1330];
60 int len, aligned_len, tmp;
61 int retval;
62
63 /* Open plaintext and param.sfo files and get size */
64
65 if (fopen_getsize(plaintext_filename, &in, &len) < 0) {
66 retval = -1;
67 goto out;
68 }
69
70 if (fopen_getsize(paramsfo_filename, &sfo, &tmp) < 0) {
71 retval = -2;
72 goto out;
73 }
74
75 /* Verify size of param.sfo; all known saves use this size */
76
77 if (tmp != 0x1330) {
78 retval = -3;
79 goto out;
80 }
81
82 /* Allocate buffers. data has 0x10 bytes extra for the IV. */
83
84 aligned_len = align16(len);
85
86 if ((data =
87 (unsigned char *) memalign(0x10, aligned_len + 0x10)) == NULL) {
88 retval = -4;
89 goto out;
90 }
91
92 if ((cryptkey = (unsigned char *) memalign(0x10, 0x10)) == NULL) {
93 retval = -5;
94 goto out;
95 }
96
97 if ((hash = (unsigned char *) memalign(0x10, 0x10)) == NULL) {
98 retval = -6;
99 goto out;
100 }
101
102 /* Fill buffers. */
103
104 memset(data + len, 0, aligned_len - len);
105 if (fread(data, 1, len, in) != len) {
106 retval = -7;
107 goto out;
108 }
109
110 if (fread(paramsfo, 1, 0x1330, sfo) != 0x1330) {
111 retval = -8;
112 goto out;
113 }
114
115 if (gamekey != NULL)
116 memcpy(cryptkey, gamekey, 0x10);
117
118 /* Do the encryption */
119
120 if ((retval = encrypt_data( gamekey ? (mainSdkVersion >= 4 ? 5 : 3) : 1, // 5 for sdk >= 4, 3 otherwise
121 data,
122 &len, &aligned_len,
123 hash,
124 gamekey ? cryptkey : NULL)) < 0) {
125 retval -= 1000;
126 goto out;
127 }
128
129 /* Update the param.sfo hashes */
130
131 if ((retval = update_hashes(paramsfo, 0x1330,
132 data_filename, hash,
133 gamekey ? 3 : 1)) < 0) {
134 retval -= 2000;
135 goto out;
136 }
137
138 /* Write the data to the file. encrypt_data has already set len. */
139
140 if ((out = fopen(encrypted_filename, "w")) == NULL) {
141 retval = -9;
142 goto out;
143 }
144
145 if (fwrite(data, 1, len, out) != len) {
146 retval = -10;
147 goto out;
148 }
149
150 /* Reopen param.sfo, and write the updated copy out. */
151
152 fclose(sfo);
153 if ((sfo = fopen(paramsfo_filename_out, "w")) == NULL) {
154 retval = -11;
155 goto out;
156 }
157
158 if (fwrite(paramsfo, 1, 0x1330, sfo) != 0x1330) {
159 retval = -12;
160 goto out;
161 }
162
163 /* All done. Return file length. */
164
165 retval = len;
166
167 out:
168 if(out) fclose(out);
169 if(hash) free(hash);
170 if(cryptkey) free(cryptkey);
171 if(data) free(data);
172 if(sfo) fclose(sfo);
173 if(in) fclose(in);
174
175 return retval;
176 }
177
178 /* Do the actual hardware encryption.
179 mode is 3 for saves with a cryptkey, or 1 otherwise
180 data, dataLen, and cryptkey must be multiples of 0x10.
181 cryptkey is NULL if mode == 1.
182 */
encrypt_data(unsigned int mode,unsigned char * data,int * dataLen,int * alignedLen,unsigned char * hash,unsigned char * cryptkey)183 int encrypt_data(unsigned int mode,
184 unsigned char *data,
185 int *dataLen,
186 int *alignedLen,
187 unsigned char *hash,
188 unsigned char *cryptkey)
189 {
190 pspChnnlsvContext1 ctx1;
191 pspChnnlsvContext2 ctx2;
192
193 /* Make room for the IV in front of the data. */
194 memmove(data + 0x10, data, *alignedLen);
195
196 /* Set up buffers */
197 memset(&ctx1, 0, sizeof(pspChnnlsvContext1));
198 memset(&ctx2, 0, sizeof(pspChnnlsvContext2));
199 memset(hash, 0, 0x10);
200 memset(data, 0, 0x10);
201
202 /* Build the 0x10-byte IV and setup encryption */
203 if (sceChnnlsv_ABFDFC8B_(&ctx2, mode, 1, data, cryptkey) < 0)
204 return -1;
205 if (sceChnnlsv_E7833020_(&ctx1, mode) < 0)
206 return -2;
207 if (sceChnnlsv_F21A1FCA_(&ctx1, data, 0x10) < 0)
208 return -3;
209 if (sceChnnlsv_850A7FA1_(&ctx2, data + 0x10, *alignedLen) < 0)
210 return -4;
211
212 /* Clear any extra bytes left from the previous steps */
213 memset(data + 0x10 + *dataLen, 0, *alignedLen - *dataLen);
214
215 /* Encrypt the data */
216 if (sceChnnlsv_F21A1FCA_(&ctx1, data + 0x10, *alignedLen) < 0)
217 return -5;
218
219 /* Verify encryption */
220 if (sceChnnlsv_21BE78B4_(&ctx2) < 0)
221 return -6;
222
223 /* Build the file hash from this PSP */
224 if (sceChnnlsv_C4C494F8_(&ctx1, hash, cryptkey) < 0)
225 return -7;
226
227 /* Adjust sizes to account for IV */
228 *alignedLen += 0x10;
229 *dataLen += 0x10;
230
231 /* All done */
232 return 0;
233 }
234