A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 315 lines 8.7 kB view raw
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}