Add COPYING file
[ps3tools.git] / pkg.c
blob7b828a93a85a3332875b7093e0e079f9eb623192
1 // Copyright 2010 Sven Peter <svenpeter@gmail.com>
2 // Licensed under the terms of the GNU GPL, version 2
3 // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
5 #include "tools.h"
6 #include "types.h"
8 #include <stdio.h>
9 #include <string.h>
10 #include <assert.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <sys/stat.h>
14 #include <zlib.h>
16 static struct key k;
17 static FILE *fp;
19 static u8 info0[0x40];
20 static u8 info1[0x40];
22 static u8 *content;
23 static u64 content_size_real;
24 static u64 content_size_compressed;
26 static u8 sce_hdr[0x20];
27 static u8 meta_hdr[0x2a0];
29 static u8 *pkg;
30 static u64 pkg_size;
32 static void get_file(u8 *bfr, const char *name, u64 size)
34 FILE *fp;
36 fp = fopen(name, "rb");
37 if (fp == NULL)
38 fail("fopen(%s) failed", name);
40 fread(bfr, size, 1, fp);
41 fclose(fp);
44 static void get_content(void)
46 u8 *base;
47 uLongf size_zlib;
48 int res;
49 struct stat st;
51 base = mmap_file("content");
53 if (stat("content", &st) < 0)
54 fail("stat(content) failed");
56 content_size_real = st.st_size;
57 content_size_compressed = compressBound(content_size_real);
58 size_zlib = content_size_compressed;
60 content = malloc(content_size_compressed);
61 if (!content)
62 fail("out of memory");
64 res = compress(content, &size_zlib, base, content_size_real);
65 if (res != Z_OK)
66 fail("compress returned %d", res);
68 content_size_compressed = size_zlib;
69 content = realloc(content, content_size_compressed);
70 if (!content)
71 fail("out of memory");
74 static void get_key(const char *suffix)
76 if (key_get(KEY_PKG, suffix, &k) < 0)
77 fail("key_get() failed");
79 if (k.pub_avail < 0)
80 fail("no public key available");
82 if (k.priv_avail < 0)
83 fail("no private key available");
85 if (ecdsa_set_curve(k.ctype) < 0)
86 fail("ecdsa_set_curve failed");
88 ecdsa_set_pub(k.pub);
89 ecdsa_set_priv(k.priv);
92 static void build_sce_hdr(void)
94 memset(sce_hdr, 0, sizeof sce_hdr);
96 wbe32(sce_hdr + 0x00, 0x53434500); // magic
97 wbe32(sce_hdr + 0x04, 2); // version
98 wbe16(sce_hdr + 0x08, 0); // dunno, sdk type?
99 wbe16(sce_hdr + 0x0a, 3); // SCE header type; pkg
100 wbe32(sce_hdr + 0x0c, 0); // meta offset
101 wbe64(sce_hdr + 0x10, sizeof sce_hdr + sizeof meta_hdr);
102 wbe64(sce_hdr + 0x18, 0x80 + content_size_real);
105 static void build_meta_hdr(void)
107 u8 *ptr;
109 memset(meta_hdr, 0, sizeof meta_hdr);
110 ptr = meta_hdr;
112 // keys for metadata encryptiomn
113 get_rand(ptr, 0x10);
114 get_rand(ptr + 0x20, 0x10);
115 ptr += 0x40;
117 // area covered by the signature
118 wbe64(ptr + 0x00, sizeof sce_hdr + sizeof meta_hdr - 0x30);
119 wbe32(ptr + 0x0c, 3); // number of encrypted headers
120 wbe32(ptr + 0x10, 3 * 8); // number of keys/hashes required
121 ptr += 0x20;
123 // first info header
124 wbe64(ptr + 0x00, 0x2c0); // offset
125 wbe64(ptr + 0x08, 0x40); // size
126 wbe32(ptr + 0x10, 1); // unknown
127 wbe32(ptr + 0x14, 1); // index
128 wbe32(ptr + 0x18, 2); // unknown again
129 wbe32(ptr + 0x1c, 0); // sha index
130 wbe32(ptr + 0x20, 1); // no encryption
131 wbe32(ptr + 0x24, 0xffffffff); // key index
132 wbe32(ptr + 0x28, 0xffffffff); // iv index
133 wbe32(ptr + 0x2c, 0x1); // no compression
134 ptr += 0x30;
136 // second info header
137 wbe64(ptr + 0x00, 0x300); // offset
138 wbe64(ptr + 0x08, 0x40); // size
139 wbe32(ptr + 0x10, 2); // unknown
140 wbe32(ptr + 0x14, 2); // index
141 wbe32(ptr + 0x18, 2); // unknown again
142 wbe32(ptr + 0x1c, 8); // sha index
143 wbe32(ptr + 0x20, 1); // no encryption
144 wbe32(ptr + 0x24, 0xffffffff); // key index
145 wbe32(ptr + 0x28, 0xffffffff); // iv index
146 wbe32(ptr + 0x2c, 0x1); // no compression
147 ptr += 0x30;
149 // package files
150 wbe64(ptr + 0x00, 0x340); // offset
151 wbe64(ptr + 0x08, content_size_compressed);
152 wbe32(ptr + 0x10, 3); // unknown
153 wbe32(ptr + 0x14, 3); // index
154 wbe32(ptr + 0x18, 2); // unknown again
155 wbe32(ptr + 0x1c, 16); // sha index
156 wbe32(ptr + 0x20, 3); // encrypted
157 wbe32(ptr + 0x24, 22); // key index
158 wbe32(ptr + 0x28, 23); // iv index
159 wbe32(ptr + 0x2c, 2); // compressed
160 ptr += 0x30;
162 // add keys/ivs and hmac keys
163 get_rand(ptr, 3 * 8 * 0x10);
166 static void fix_info_hdr(void)
168 wbe64(info0 + 0x18, content_size_real);
169 wbe64(info0 + 0x20, content_size_compressed);
170 wbe64(info1 + 0x20, content_size_compressed);
173 static void build_pkg(void)
175 pkg_size = sizeof sce_hdr + sizeof meta_hdr + 0x80;
176 pkg_size += content_size_compressed;
177 pkg_size = round_up(pkg_size, 0x100);
179 pkg = malloc(pkg_size);
180 if (!pkg)
181 fail("out of memory");
183 memset(pkg, 0xaa, pkg_size);
184 memcpy(pkg, sce_hdr, 0x20);
185 memcpy(pkg + 0x20, meta_hdr, 0x2a0);
186 memcpy(pkg + 0x2c0, info0, 0x40);
187 memcpy(pkg + 0x300, info1, 0x40);
188 memcpy(pkg + 0x340, content, content_size_compressed);
191 static void calculate_hash(u8 *data, u64 len, u8 *digest)
193 memset(digest, 0, 0x20);
194 sha1_hmac(digest + 0x20, data, len, digest);
197 static void hash_pkg(void)
199 calculate_hash(pkg + 0x2c0, 0x40, pkg + 0x80 + 3*0x30);
200 calculate_hash(pkg + 0x300, 0x40, pkg + 0x80 + 3*0x30 + 8*0x10);
201 calculate_hash(pkg + 0x340, content_size_compressed,
202 pkg + 0x80 + 3*0x30 + 16*0x10);
205 static void sign_pkg(void)
207 u8 *r, *s;
208 u8 hash[20];
209 u64 sig_len;
211 sig_len = be64(pkg + 0x60);
212 r = pkg + sig_len;
213 s = r + 21;
215 sha1(pkg, sig_len, hash);
217 ecdsa_sign(hash, r, s);
220 int main(int argc, char *argv[])
222 if (argc != 4)
223 fail("usage: pkg [key suffix] [contents] [filename.pkg]");
225 fp = fopen(argv[3], "wb");
226 if (fp == NULL)
227 fail("fopen(%s) failed", argv[3]);
229 if (chdir(argv[2]) < 0)
230 fail("chdir");
232 get_key(argv[1]);
233 get_file(info0, "info0", 0x40);
234 get_file(info1, "info1", 0x40);
235 get_content();
237 build_sce_hdr();
238 build_meta_hdr();
239 fix_info_hdr();
241 build_pkg();
242 hash_pkg();
243 sign_pkg();
245 sce_encrypt_data(pkg);
246 sce_encrypt_header(pkg, &k);
248 fwrite(pkg, pkg_size, 1, fp);
249 fclose(fp);
251 return 0;