A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 *
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
9 *
10 * $Id$
11 *
12 * Rockbox plugin copyright (C) 2009 Dave Chapman.
13 * Based on encryption code (C) 2009 Michael Sparmann
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25/*
26
27 This viewer plugin is for the encryption/decryption of iPod Nano
28 (2nd generation) firmware images using the hardware AES crypto unit
29 in such devices.
30
31 Encrypted images are stored with the modelname "nn2x" and extension
32 ".ipodx" Unencrypted images use "nn2g" and ".ipod".
33
34 Heavily based on Payloads/CryptFirmware/main.c from iBugger.
35
36*/
37
38#include "plugin.h"
39#include "checksum.h"
40
41static void aes_encrypt(void* data, uint32_t size)
42{
43 uint32_t ptr, i;
44 uint32_t go = 1;
45 PWRCONEXT &= ~0x400;
46 AESTYPE = 1;
47 AESUNKREG0 = 1;
48 AESUNKREG0 = 0;
49 AESCONTROL = 1;
50 AESKEYLEN = 9;
51 AESOUTSIZE = size;
52 AESAUXSIZE = 0x10;
53 AESINSIZE = 0x10;
54 AESSIZE3 = 0x10;
55 for (ptr = 0; ptr < (size >> 2); ptr += 4)
56 {
57 AESOUTADDR = (uint32_t)data + (ptr << 2);
58 AESINADDR = (uint32_t)data + (ptr << 2);
59 AESAUXADDR = (uint32_t)data + (ptr << 2);
60 if (ptr != 0)
61 for (i = 0; i < 4; i++)
62 ((uint32_t*)data)[ptr + i] ^= ((uint32_t*)data)[ptr + i - 4];
63 AESSTATUS = 6;
64 AESGO = go;
65 go = 3;
66 while ((AESSTATUS & 6) == 0);
67 }
68 AESCONTROL = 0;
69 PWRCONEXT |= 0x400;
70}
71
72static void aes_decrypt(void* data, uint32_t size)
73{
74 uint32_t ptr, i;
75 uint32_t go = 1;
76 PWRCONEXT &= ~0x400;
77 AESTYPE = 1;
78 AESUNKREG0 = 1;
79 AESUNKREG0 = 0;
80 AESCONTROL = 1;
81 AESKEYLEN = 8;
82 AESOUTSIZE = size;
83 AESAUXSIZE = 0x10;
84 AESINSIZE = 0x10;
85 AESSIZE3 = 0x10;
86 for (ptr = (size >> 2) - 4; ; ptr -= 4)
87 {
88 AESOUTADDR = (uint32_t)data + (ptr << 2);
89 AESINADDR = (uint32_t)data + (ptr << 2);
90 AESAUXADDR = (uint32_t)data + (ptr << 2);
91 AESSTATUS = 6;
92 AESGO = go;
93 go = 3;
94 while ((AESSTATUS & 6) == 0);
95 if (ptr == 0) break;
96 for (i = 0; i < 4; i++)
97 ((uint32_t*)data)[ptr + i] ^= ((uint32_t*)data)[ptr + i - 4];
98 }
99 AESCONTROL = 0;
100 PWRCONEXT |= 0x400;
101}
102
103static void calc_hash(uint32_t* data, uint32_t size, uint32_t* result)
104{
105 uint32_t ptr, i;
106 uint32_t config = 2;
107
108 PWRCONEXT &= ~0x4;
109
110 for (ptr = 0; ptr < (size >> 2); ptr += 0x10)
111 {
112 for (i = 0; i < 0x10; i++) SHA1DATAIN[i] = data[ptr + i];
113 SHA1CONFIG = config;
114 config = 0xA;
115 while ((SHA1CONFIG & 1) != 0);
116 }
117 for (i = 0; i < 5; i ++) result[i] = SHA1RESULT[i];
118
119 PWRCONEXT |= 0x4;
120}
121
122static uint32_t get_uint32be(unsigned char* buf)
123{
124 return (uint32_t)((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]);
125}
126
127static void put_uint32be(unsigned char* buf, uint32_t x)
128{
129 buf[0] = (x & 0xff000000) >> 24;
130 buf[1] = (x & 0xff0000) >> 16;
131 buf[2] = (x & 0xff00) >> 8;
132 buf[3] = x & 0xff;
133}
134
135enum plugin_status plugin_start(const void* parameter)
136{
137 int fd;
138 int length;
139 int n;
140 ssize_t buf_size;
141 uint32_t* buf;
142 int size;
143 uint32_t sum;
144 uint32_t hash[0x200];
145 char outputfilename[MAX_PATH];
146
147 fd = rb->open(parameter,O_RDONLY);
148
149 if (fd < 0) {
150 rb->splash(HZ*2, "Cannot open file");
151 return PLUGIN_ERROR;
152 }
153
154 length = rb->filesize(fd);
155
156 if (length < 12) {
157 rb->splash(HZ*2, "File too small");
158 return PLUGIN_ERROR;
159 }
160
161 /* Get the audio buffer */
162 buf = rb->plugin_get_audio_buffer((size_t *)&buf_size);
163
164 /* Use uncached alias for buf - equivalent to buf |= 0x40000000 */
165 buf += 0x10000000;
166
167 if (length > buf_size) {
168 rb->splash(HZ*2, "File too big");
169 return PLUGIN_ERROR;
170 }
171
172 n = rb->read(fd, buf, length);
173 if (n < length) {
174 rb->splash(HZ*2, "Cannot read file");
175 return PLUGIN_ERROR;
176 }
177 rb->close(fd);
178
179 size = length - 8; /* Size of firmware image */
180
181 if (calc_checksum(MODEL_NUMBER, (unsigned char*)(buf + 2), size) !=
182 get_uint32be((unsigned char*)buf)) {
183 rb->splash(HZ*2, "Bad checksum in input file");
184 return PLUGIN_ERROR;
185 }
186
187 n = rb->strlen(parameter);
188 if (memcmp(buf+1,"nn2g",4)==0) {
189 /* Encrypting - Input file should be .ipod, output file is .ipodx */
190
191 if ((n < 6) || (rb->strcmp(parameter+n-5,".ipod") != 0)) {
192 rb->splash(HZ*2, "Input filename must be .ipod");
193 return PLUGIN_ERROR;
194 }
195
196 if (n + 2 > MAX_PATH) {
197 rb->splash(HZ*2, "Filename too long");
198 return PLUGIN_ERROR;
199 }
200
201 size = (size + 0x3f) & ~0x3f; /* Pad to multiple of 64 bytes */
202 if (size > (length - 8)) {
203 rb->memset(&buf[length/4], 0, size - (length - 8));
204 }
205
206 rb->strlcpy(outputfilename, parameter, MAX_PATH);
207 outputfilename[n] = 'x';
208 outputfilename[n+1] = 0;
209
210 /* Everything is OK, now do the encryption */
211
212 /* 1 - Calculate hashes */
213
214 rb->memset(hash, 0, sizeof(hash));
215
216 hash[1] = 2;
217 hash[2] = 1;
218 hash[3] = 0x40;
219 hash[5] = size;
220
221 calc_hash(buf + 2, size, &hash[7]);
222 calc_hash(hash, 0x200, &hash[0x75]);
223
224 /* 2 - Do the encryption */
225
226 rb->splash(0, "Encrypting...");
227 aes_encrypt(buf + 2, size);
228
229 /* 3 - Update the Rockbox header */
230
231 sum = calc_checksum(MODEL_NUMBER, (unsigned char*)hash, sizeof(hash));
232 sum = calc_checksum(sum, (unsigned char*)(buf + 2), size);
233 put_uint32be((unsigned char*)buf, sum);
234 memcpy(buf + 1, "nn2x", 4);
235
236 /* 4 - Write to disk */
237 fd = rb->open(outputfilename,O_WRONLY|O_CREAT|O_TRUNC, 0666);
238
239 if (fd < 0) {
240 rb->splash(HZ*2, "Could not open output file");
241 return PLUGIN_ERROR;
242 }
243
244 n = rb->write(fd, buf, 8);
245 n = rb->write(fd, hash, sizeof(hash));
246 n = rb->write(fd, buf + 2, size);
247
248 rb->close(fd);
249 } else if (memcmp(buf + 1,"nn2x",4)==0) {
250 /* Decrypting - Input file should be .ipodx, output file is .ipod */
251
252 if ((n < 7) || (rb->strcmp(parameter+n-6,".ipodx") != 0)) {
253 rb->splash(HZ*2, "Input filename must be .ipodx");
254 return PLUGIN_ERROR;
255 }
256
257 rb->strlcpy(outputfilename, parameter, MAX_PATH);
258 outputfilename[n-1] = 0; /* Remove "x" at end of filename */
259
260 /* Everything is OK, now do the decryption */
261
262 size -= 0x800; /* Remove hash size from firmware size */
263
264 /* 1 - Decrypt */
265
266 rb->splash(0, "Decrypting...");
267
268 aes_decrypt(&buf[0x202], size);
269
270 /* 2 - Calculate hashes to verify decryption */
271
272 rb->lcd_clear_display();
273 rb->splash(0, "Calculating hash...");
274
275 rb->memset(hash, 0, sizeof(hash));
276
277 hash[1] = 2;
278 hash[2] = 1;
279 hash[3] = 0x40;
280 hash[5] = size;
281
282 calc_hash(&buf[0x202], size, &hash[7]);
283 calc_hash(hash, 0x200, &hash[0x75]);
284
285 if ((memcmp(hash + 7, buf + 9, 20) != 0) ||
286 (memcmp(hash + 75, buf + 77, 20) != 0)) {
287 rb->splash(HZ*2, "Decryption failed - bad hash");
288 return PLUGIN_ERROR;
289 }
290
291 /* 3 - Update the Rockbox header */
292
293 sum = calc_checksum(MODEL_NUMBER, (unsigned char*)(&buf[0x202]), size);
294 put_uint32be((unsigned char*)buf, sum);
295 memcpy(buf + 1, "nn2g", 4);
296
297 /* 4 - Write to disk */
298 fd = rb->open(outputfilename,O_WRONLY|O_CREAT|O_TRUNC, 0666);
299
300 if (fd < 0) {
301 rb->splash(HZ*2, "Could not open output file");
302 return PLUGIN_ERROR;
303 }
304
305 n = rb->write(fd, buf, 8);
306 n = rb->write(fd, &buf[0x202], size);
307
308 rb->close(fd);
309 } else {
310 rb->splash(HZ*2,"Invalid input file");
311 return PLUGIN_ERROR;
312 }
313
314 return PLUGIN_OK;
315}