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