├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── config.mk.template ├── crypto.c ├── crypto.h ├── download.c ├── download.h ├── main.c ├── main.h ├── types.h ├── util.c ├── util.h └── version.h /.gitignore: -------------------------------------------------------------------------------- 1 | config.mk 2 | nustool 3 | nustool.exe 4 | *.o 5 | *.dll 6 | bin -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2016, SciresM 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include config.mk 2 | 3 | .PHONY: clean 4 | 5 | CFLAGS += -D_BSD_SOURCE -D_POSIX_SOURCE -D_POSIX_C_SOURCE=2 -D__USE_MINGW_ANSI_STDIO=1 6 | 7 | all: nustool 8 | 9 | .c.o: 10 | $(CC) -c $(CFLAGS) -o $@ $< 11 | 12 | nustool: crypto.o main.o download.o util.o 13 | $(CC) -o $@ $^ $(LDFLAGS) 14 | 15 | crypto.o: crypto.h types.h 16 | 17 | main.o: main.c download.h types.h util.h 18 | 19 | download.c: download.h types.h util.h 20 | 21 | util.o: util.c util.h types.h version.h 22 | 23 | clean: 24 | rm -f *.o nustool nustool.exe 25 | 26 | dist: 27 | $(eval NUSTOOLVER = $(shell grep '\bNUSTOOL_VERSION\b' version.h \ 28 | | cut -d' ' -f2 \ 29 | | sed -e 's/"//g')) 30 | mkdir nustool-$(NUSTOOLVER) 31 | cp *.c *.h config.mk Makefile README.md LICENSE nustool-$(NUSTOOLVER) 32 | tar czf nustool-$(NUSTOOLVER).tar.gz nustool-$(NUSTOOLVER) 33 | rm -r nustool-$(NUSTOOLVER) 34 | 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nustool 2 | 3 | nustool is a simple downloader and decryptor for titles on the Nintendo Update 4 | Servers (NUS). 5 | 6 | ## Usage 7 | 8 | ``` 9 | Usage: nustool [-cDmpr] [-k decrypted_key] [-K encrypted_key] 10 | [-V version] titleid 11 | 12 | Downloads and optionally decrypts a title from NUS. 13 | 14 | -c try to decrypt the title using the CETK key 15 | -D if decrypting the title using the CETK key, use the 16 | development common key to decrypt the titlekey 17 | -k [key] the titlekey to use to decrypt the contents 18 | -K [key] the encrypted titlekey to use to decrypt the contents 19 | -h print this help and exit 20 | -l download files to local directory instead of /tid/ver/ 21 | -m keep meta files (cetk, tmd); usable with make_cdn_cia 22 | -p show progress bars 23 | -r resume download 24 | -v print nustool version and exit 25 | -V [version] the version of the title to download; if not given, 26 | the latest version will be downloaded 27 | 28 | If none of -c, -k and -K are given, the raw encrypted contents 29 | will be downloaded. 30 | 31 | All files are downloaded into a titleid/version directory. 32 | ``` 33 | 34 | ## Building 35 | 36 | Copy `config.mk.template` to `config.mk` and then run `make`. 37 | 38 | To build under windows, you will need to build [libcurl](https://curl.haxx.se/libcurl/), [libgpgerror](https://www.gnupg.org/(fr)/related_software/libgpg-error/index.html), 39 | and [libgcrypt](https://www.gnu.org/software/libgcrypt/). I recommend using MinGW. 40 | 41 | ## Environment Variables 42 | 43 | You can set the NUSTOOL\_BASE\_URL environment variable to point to a different 44 | CDN (e.g. for a local mirror). If not set, it'll default to 45 | `http://ccs.cdn.c.shop.nintendowifi.net/ccs/download`. 46 | 47 | ## Examples 48 | 49 | ### Downloading the raw encrypted contents of title 0004001b00010002 50 | 51 | `$ nustool 0004001b00010002` 52 | 53 | ### Downloading and decrypting a system title 54 | 55 | For system titles, you can pass `-c` to have them automatically be decrypted 56 | using each system's common key. Note that this is not possible for 3DS system 57 | titles as the required common keyX (or rather, the bootroms) has not yet been 58 | dumped. 59 | 60 | `$ nustool -c 0003000f484e4c45` 61 | 62 | ### Downloading and decrypting title 0006000012345678 with the encrypted titlekey 0123456789abcdef0123456789abcdef 63 | 64 | `$ nustool -K 0123456789abcdef0123456789abcdef 0006000012345678` 65 | 66 | ### Downloading and decrypting title 0006000012345678 with the decrypted titlekey abcdef0123456789abcdef0123456789 67 | 68 | `$ nustool -k abcdef0123456789abcdef0123456789 0006000012345678` 69 | 70 | ### Downloading and decrypting system title 00030017484e414a, displaying a progress bar for each file, version 1280 71 | 72 | `$ nustool -cpV 1280 00030017484e414a` 73 | 74 | ## Licensing 75 | 76 | This software is licensed under the terms of the ISC License. 77 | You can find a copy of the license in the LICENSE file. 78 | 79 | -------------------------------------------------------------------------------- /config.mk.template: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-g -O0 -Wall -Wextra -pedantic -std=c99 -fPIC 3 | LDFLAGS= -lcurl -lgcrypt 4 | 5 | -------------------------------------------------------------------------------- /crypto.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "crypto.h" 4 | #include "types.h" 5 | 6 | static void rotl(byte *buf) 7 | { 8 | byte carry = 0; 9 | size_t i = 16; 10 | bool set_carry; 11 | 12 | do { 13 | set_carry = (buf[i - 1] & 0x80); 14 | 15 | buf[i - 1] = (byte)(buf[i - 1] << 1) | carry; 16 | 17 | if (set_carry) 18 | carry = 1; 19 | else 20 | carry = 0; 21 | } while (--i > 0); 22 | 23 | buf[15] |= carry; 24 | } 25 | 26 | static void rotl128(byte *buf, size_t bits) 27 | { 28 | do { 29 | rotl(buf); 30 | } while (--bits > 0); 31 | } 32 | 33 | static void xor128(byte *out, const byte *a, const byte *b) 34 | { 35 | for (size_t i = 0; i < 16; ++i) 36 | out[i] = a[i] ^ b[i]; 37 | } 38 | 39 | static void add128(byte *out, const byte *a, const byte *b) 40 | { 41 | byte carry = 0; 42 | size_t i = 16; 43 | 44 | do { 45 | out[i - 1] = a[i - 1] + b[i - 1] + carry; 46 | carry = (out[i - 1] < a[i - 1]); 47 | } while (--i > 0); 48 | } 49 | 50 | void *crypto_ctr_key_scramble(void *out, const void *keyX, const void *keyY) 51 | { 52 | static const byte c[16] = { 53 | 0x1F, 0xF9, 0xE9, 0xAA, 0xC5, 0xFE, 0x04, 0x08, 54 | 0x02, 0x45, 0x91, 0xDC, 0x5D, 0x52, 0x76, 0x8A 55 | }; 56 | byte x[16]; 57 | byte y[16]; 58 | 59 | memcpy(x, keyX, sizeof(x)); 60 | memcpy(y, keyY, sizeof(y)); 61 | 62 | rotl128(x, 2); 63 | xor128(out, x, y); 64 | memcpy(x, out, sizeof(x)); 65 | add128(out, x, c); 66 | rotl128(out, 87); 67 | 68 | return out; 69 | } 70 | 71 | -------------------------------------------------------------------------------- /crypto.h: -------------------------------------------------------------------------------- 1 | #ifndef NUSTOOL_CRYPTO_H 2 | #define NUSTOOL_CRYPTO_H 3 | 4 | #define GCRYPT_NO_DEPRECATED 5 | #include 6 | 7 | void *crypto_ctr_key_scramble(void *out, const void *keyX, const void *keyY); 8 | 9 | #endif 10 | 11 | -------------------------------------------------------------------------------- /download.c: -------------------------------------------------------------------------------- 1 | #ifdef __WIN32 2 | #define CURL_STATICLIB 3 | #endif 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "crypto.h" 15 | #include "main.h" 16 | #include "download.h" 17 | #include "types.h" 18 | #include "util.h" 19 | 20 | #ifdef _WIN32 21 | #define CURL_INIT_VARS CURL_GLOBAL_WIN32 22 | #else 23 | #define CURL_INIT_VARS CURL_GLOBAL_NOTHING 24 | #endif 25 | 26 | static CURL *curl; 27 | static char errbuf[CURL_ERROR_SIZE]; 28 | 29 | static byte *tmd = NULL; 30 | static size_t tmdsize; 31 | static byte *cetk = NULL; 32 | static size_t cetksize; 33 | static uint8_t common_key_index; 34 | static struct Content *contents = NULL; 35 | 36 | static const char *curlerrstr(CURLcode code) 37 | { 38 | if (*errbuf) 39 | return errbuf; 40 | else 41 | return curl_easy_strerror(code); 42 | } 43 | 44 | static const char *get_cdn_base_url(void) 45 | { 46 | const char *ret; 47 | 48 | if ((ret = getenv("NUSTOOL_BASE_URL")) == NULL) 49 | ret = DEFAULT_NUS_BASE_URL; 50 | 51 | return ret; 52 | } 53 | 54 | static char *get_base_url_for_titleid(uint64_t titleid) 55 | { 56 | size_t len; 57 | const char *baseurl; 58 | char *urlbuf; 59 | 60 | baseurl = get_cdn_base_url(); 61 | 62 | /* baseurl + "/0000000000000000/" */ 63 | len = strlen(baseurl) + 18; 64 | 65 | urlbuf = malloc(len + 1); 66 | 67 | sprintf(urlbuf, "%s/%016" PRIx64 "/", baseurl, titleid); 68 | 69 | return urlbuf; 70 | } 71 | 72 | static char *get_tmd_url(void) 73 | { 74 | char *urlbuf; 75 | 76 | if ((urlbuf = get_base_url_for_titleid(opts.titleid)) == NULL) 77 | return NULL; 78 | 79 | if (opts.flags & OPT_HAS_VERSION) 80 | return util_realloc_and_append_fmt(urlbuf, 9, "tmd.%" PRIu16, 81 | opts.version); 82 | else 83 | return util_realloc_and_append_fmt(urlbuf, 3, "%s", "tmd"); 84 | } 85 | 86 | static char *get_cetk_url(void) 87 | { 88 | char *urlbuf; 89 | 90 | if ((urlbuf = get_base_url_for_titleid(opts.titleid)) == NULL) 91 | return NULL; 92 | 93 | return util_realloc_and_append_fmt(urlbuf, 4, "%s", "cetk"); 94 | } 95 | 96 | static char *get_content_url(const struct Content *content) 97 | { 98 | char *urlbuf; 99 | 100 | if ((urlbuf = get_base_url_for_titleid(opts.titleid)) == NULL) 101 | return NULL; 102 | 103 | return util_realloc_and_append_fmt(urlbuf, 8, "%08" PRIx32, 104 | content->contentid); 105 | } 106 | 107 | static char *get_h3_url_for_content(const struct Content *content) 108 | { 109 | char *urlbuf; 110 | 111 | if ((urlbuf = get_base_url_for_titleid(opts.titleid)) == NULL) 112 | return NULL; 113 | 114 | return util_realloc_and_append_fmt(urlbuf, 11, "%08" PRIx32 ".h3", 115 | content->contentid); 116 | } 117 | 118 | 119 | static errno_t download_init(void) 120 | { 121 | CURLcode code; 122 | 123 | /* Forward-compatibility: CURL_GLOBAL_SSL needs setting if Nintendo ever 124 | * switches to a TLS/SSL CDN. 125 | */ 126 | if ((code = curl_global_init(CURL_INIT_VARS)) != CURLE_OK) { 127 | err("curl_global_init: %s", curl_easy_strerror(code)); 128 | return -1; 129 | } 130 | 131 | if ((curl = curl_easy_init()) == NULL) 132 | return -1; 133 | 134 | if ((code = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf)) 135 | != CURLE_OK) { 136 | err("curl_easy_setopt(ERRORBUFFER): %s", 137 | curlerrstr(code)); 138 | return -1; 139 | } 140 | 141 | if ((code = curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 142 | !(opts.flags & OPT_SHOW_PROGRESS))) != CURLE_OK) { 143 | err("curl_easy_setopt(NOPROGRESS): %s", 144 | curlerrstr(code)); 145 | return -1; 146 | } 147 | 148 | if ((code = curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1)) 149 | != CURLE_OK) { 150 | err("curl_easy_setopt(FAILONERROR): %s", 151 | curlerrstr(code)); 152 | return -1; 153 | } 154 | 155 | if ((code = curl_easy_setopt(curl, CURLOPT_USERAGENT, 156 | "CTR/P/1.0.0/r61631")) 157 | != CURLE_OK) { 158 | err("curl_easy_setopt(USERAGENT): %s", 159 | curlerrstr(code)); 160 | return -1; 161 | } 162 | 163 | return 0; 164 | } 165 | 166 | static struct Content *make_content(void) 167 | { 168 | struct Content *content; 169 | 170 | if ((content = malloc(sizeof(*content))) == NULL) 171 | oom(); 172 | 173 | content->next = contents; 174 | contents = content; 175 | 176 | return content; 177 | } 178 | 179 | static inline bool is_valid_sig_type(uint32_t type) 180 | { 181 | return (type == 0 182 | || (type > SIG_UNDERFLOW && type < SIG_OVERFLOW)); 183 | } 184 | 185 | static inline ssize_t bytes_to_skip_for_signature(uint32_t sigtype) 186 | { 187 | enum SignatureTypeID type; 188 | 189 | if (!is_valid_sig_type(sigtype)) 190 | return -1; 191 | 192 | /* This seems to happen with some Ambassador tickets. */ 193 | if (sigtype == 0) 194 | type = RSA_2048_SHA_256; 195 | else 196 | type = (enum SignatureTypeID)sigtype; 197 | 198 | switch (type) { 199 | /* Unused. */ 200 | case RSA_4096_SHA_1: 201 | case RSA_4096_SHA_256: 202 | return 0x23c; 203 | 204 | /* Wii, DSi, maybe Wii U */ 205 | case RSA_2048_SHA_1: 206 | /* 3DS */ 207 | case RSA_2048_SHA_256: 208 | return 0x13c; 209 | 210 | /* Unused. */ 211 | case EC_SHA_1: 212 | case EC_SHA_256: 213 | return 0x7c; 214 | 215 | case SIG_UNDERFLOW: 216 | case SIG_OVERFLOW: 217 | return -1; 218 | } 219 | 220 | return -1; 221 | } 222 | 223 | /* Indexed by TMD version. */ 224 | static struct TMDFileFormat tmd_format[] = { 225 | /* TMD version = 0 (Wii, DSi) */ 226 | { 227 | .ncontents_offset = 0x9e, 228 | .contents_offset = 0xa4, 229 | .title_version_offset = 0x9c, 230 | .content_chunk_len = 0x24, 231 | .hash_len = 0x14 232 | }, 233 | /* TMD version = 1 (3DS, Wii U) */ 234 | { 235 | .ncontents_offset = 0x9e, 236 | .contents_offset = 0x9c4, 237 | .title_version_offset = 0x9c, 238 | .content_chunk_len = 0x30, 239 | .hash_len = 0x20 240 | } 241 | }; 242 | 243 | static errno_t write_file_from_memory(const char *filename, 244 | void *data, size_t datalen) 245 | { 246 | FILE *f; 247 | 248 | char *filepath = util_get_filepath(filename); 249 | 250 | /* We may get an attempt to write a 0-len cetk if we run with -m and 251 | * download a title that has no cetk. 252 | */ 253 | if (datalen == 0) { 254 | free(filepath); 255 | return 0; 256 | } 257 | 258 | if ((f = fopen(filepath, "wb")) == NULL) { 259 | err("Unable to open %s for writing: %s", filepath, 260 | strerror(errno)); 261 | free(filepath); 262 | return -1; 263 | } 264 | 265 | fwrite(data, datalen, 1, f); 266 | 267 | fclose(f); 268 | free(filepath); 269 | 270 | return 0; 271 | } 272 | 273 | static errno_t build_contents_list_and_create_outdir(void) 274 | { 275 | uint32_t sigtype = ((uint32_t)tmd[0] << 24) | ((uint32_t)tmd[1] << 16) 276 | | ((uint32_t)tmd[2] << 8) | tmd[3]; 277 | ssize_t offset; 278 | struct Content *content; 279 | struct TMDFileFormat *format; 280 | byte *ptr; 281 | size_t ncontents; 282 | uint16_t tver; 283 | 284 | if ((offset = bytes_to_skip_for_signature(sigtype)) == -1) { 285 | err("Error: Unknown signature type %" PRIx32, sigtype); 286 | return -1; 287 | } 288 | 289 | /* Account for the initial four bytes used for the signature type. */ 290 | offset += 4; 291 | 292 | if (tmd[offset + 0x40] > 0x1) { 293 | err("Warning: Unknown TMD version %" PRIu8 ", aborting.", 294 | tmd[offset + 0x40]); 295 | return -1; 296 | } 297 | 298 | format = &tmd_format[tmd[offset + 0x40]]; 299 | 300 | tver = (uint16_t)((tmd[(size_t)offset + format->title_version_offset] 301 | << 8) 302 | | tmd[(size_t)offset + format->title_version_offset + 1]); 303 | 304 | if (tver != opts.version && (opts.flags & OPT_HAS_VERSION)) 305 | err("Warning: TMD version does not match requested version!"); 306 | 307 | opts.version = tver; 308 | 309 | /* Create output directory, if relevant. */ 310 | if (!(opts.flags & OPT_LOCAL_FILES) && util_create_outdir() != 0) { 311 | err("Failed to create output directory"); 312 | return -1; 313 | } 314 | 315 | ncontents = ((size_t)tmd[(size_t)offset + format->ncontents_offset] << 8) 316 | | tmd[(size_t)offset + format->ncontents_offset + 1]; 317 | 318 | ptr = tmd + offset + format->contents_offset; 319 | for (size_t i = 0; i < ncontents; ++i) { 320 | if (ptr + format->content_chunk_len > tmd + tmdsize) { 321 | err("Attempted to read TMD out of range."); 322 | return -1; 323 | } 324 | 325 | if ((content = make_content()) == NULL) 326 | oom(); 327 | 328 | content->contentid = ((uint32_t)ptr[0] << 24) | ((uint32_t)ptr[1] << 16) 329 | | ((uint32_t)ptr[2] << 8) | ptr[3]; 330 | content->idx[0] = ptr[4]; 331 | content->idx[1] = ptr[5]; 332 | 333 | content->type = (uint16_t)(((uint16_t)ptr[6] << 8) | ptr[7]); 334 | 335 | content->size = ((uint64_t)ptr[8] << 56) | 336 | ((uint64_t)ptr[9] << 48) | 337 | ((uint64_t)ptr[10] << 40) | 338 | ((uint64_t)ptr[11] << 32) | 339 | ((uint64_t)ptr[12] << 24) | 340 | ((uint64_t)ptr[13] << 16) | 341 | ((uint64_t)ptr[14] << 8) | 342 | ptr[15]; 343 | 344 | memcpy(content->hash, ptr + 16, 0x20); 345 | 346 | ptr += format->content_chunk_len; 347 | } 348 | 349 | return 0; 350 | } 351 | 352 | static size_t download_tmd_cb(char *ptr, size_t size, size_t nmemb, 353 | void *userdata) 354 | { 355 | struct DownloadState *ds = userdata; 356 | byte *data = (byte *)ptr; 357 | size_t datalen = size * nmemb; 358 | 359 | if (nmemb != 0 && datalen / nmemb != size) { 360 | ds->flags |= DS_ERROR; 361 | err("Error: Overflow while calculating downloaded size."); 362 | return 0; 363 | } 364 | 365 | /* Only happens on empty file */ 366 | if (datalen == 0) { 367 | ds->flags |= DS_ERROR; 368 | err("Error: Received 0-length TMD from remote."); 369 | return 0; 370 | } 371 | 372 | if (tmd != NULL) { 373 | if (tmdsize + datalen < tmdsize) { 374 | ds->flags |= DS_ERROR; 375 | err("Overflow while calculating downloaded size."); 376 | return 0; 377 | } 378 | 379 | if ((tmd = realloc(tmd, tmdsize + datalen)) == NULL) 380 | oom(); 381 | 382 | memcpy(tmd + tmdsize, data, datalen); 383 | 384 | tmdsize += datalen; 385 | } else { 386 | if ((tmd = malloc(datalen)) == NULL) 387 | oom(); 388 | 389 | memcpy(tmd, data, datalen); 390 | tmdsize = datalen; 391 | } 392 | 393 | return datalen; 394 | } 395 | 396 | static errno_t download_tmd(void) 397 | { 398 | struct DownloadState ds = {.flags = 0}; 399 | CURLcode code; 400 | char *url; 401 | 402 | if ((url = get_tmd_url()) == NULL) 403 | oom(); 404 | 405 | if ((code = curl_easy_setopt(curl, CURLOPT_URL, url)) != CURLE_OK) { 406 | err("curl_easy_setopt(URL:tmd): %s", 407 | curlerrstr(code)); 408 | free(url); 409 | return -1; 410 | } 411 | 412 | if ((code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ds)) 413 | != CURLE_OK) { 414 | err("curl_easy_setopt(WRITEDATA): %s", 415 | curlerrstr(code)); 416 | free(url); 417 | return -1; 418 | } 419 | 420 | if ((code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, 421 | download_tmd_cb)) != CURLE_OK) { 422 | err("curl_easy_setopt(WRITEFUNCTION): %s", 423 | curlerrstr(code)); 424 | free(url); 425 | return -1; 426 | } 427 | 428 | *errbuf = '\0'; 429 | 430 | if (opts.flags & OPT_SHOW_PROGRESS) 431 | msg("Downloading TMD..."); 432 | 433 | if ((code = curl_easy_perform(curl)) != CURLE_OK) { 434 | err("curl_easy_perform(tmd): %s", 435 | curlerrstr(code)); 436 | free(url); 437 | return -1; 438 | } 439 | 440 | free(url); 441 | 442 | if (ds.flags & DS_ERROR) 443 | return -1; 444 | 445 | if (build_contents_list_and_create_outdir() != 0) 446 | return -1; 447 | 448 | return 0; 449 | } 450 | 451 | static inline const byte *get_common_keyY_ctr(uint64_t titleid, bool is_retail) 452 | { 453 | /* The 3DS uses a field in the ticket to determine which keyY to use. 454 | * 455 | * Normally, only 0 (application, e.g. from eShop) and 1 (system title) 456 | * are used. Appearances of the other keyYs haven't been spotted. 457 | * 458 | * However, we may be provided the encrypted titlekey from -K only. In 459 | * that case, we have no ticket, so we'll just test if TIDhigh bit 0x10, 460 | * which indicates a system title, is set. 461 | */ 462 | static const byte ctr_ckeyYs[6][16] = { 463 | {0xd0, 0x7b, 0x33, 0x7f, 0x9c, 0xa4, 0x38, 0x59, 0x32, 0xa2, 0xe2, 0x57, 0x23, 0x23, 0x2e, 0xb9}, 464 | {0x0c, 0x76, 0x72, 0x30, 0xf0, 0x99, 0x8f, 0x1c, 0x46, 0x82, 0x82, 0x02, 0xfa, 0xac, 0xbe, 0x4c}, 465 | {0xc4, 0x75, 0xcb, 0x3a, 0xb8, 0xc7, 0x88, 0xbb, 0x57, 0x5e, 0x12, 0xa1, 0x09, 0x07, 0xb8, 0xa4}, 466 | {0xe4, 0x86, 0xee, 0xe3, 0xd0, 0xc0, 0x9c, 0x90, 0x2f, 0x66, 0x86, 0xd4, 0xc0, 0x6f, 0x64, 0x9f}, 467 | {0xed, 0x31, 0xba, 0x9c, 0x04, 0xb0, 0x67, 0x50, 0x6c, 0x44, 0x97, 0xa3, 0x5b, 0x78, 0x04, 0xfc}, 468 | {0x5e, 0x66, 0x99, 0x8a, 0xb4, 0xe8, 0x93, 0x16, 0x06, 0x85, 0x0f, 0xd7, 0xa1, 0x6d, 0xd7, 0x55} 469 | }; 470 | /* Titles that use keyY#0 (application) use a different keyY in the 471 | * development environment. 472 | * 473 | * (Technically, the 3DS firmware uses a hardcoded normalkey, rather 474 | * than computing it from keyX and keyY, but we do it differently here 475 | * for the sake of having less branching.) 476 | */ 477 | static const byte ctr_ckeyY_dev_app[16] = { 478 | 0x85, 0x21, 0x5e, 0x96, 0xcb, 0x95, 0xa9, 0xec, 0xa4, 0xb4, 0xde, 0x60, 0x1c, 0xb5, 0x62, 0xc7 479 | }; 480 | 481 | if (cetksize == 0) { 482 | if ((titleid >> 32) & 0x10) 483 | common_key_index = 1; 484 | else 485 | common_key_index = 0; 486 | } 487 | 488 | if (common_key_index > 5) { 489 | err("Error: Unknown 3DS common keyY index %" PRIu8 "?!", 490 | common_key_index); 491 | return NULL; 492 | } 493 | 494 | if (!is_retail && common_key_index == 0) 495 | return ctr_ckeyY_dev_app; 496 | else 497 | return ctr_ckeyYs[common_key_index]; 498 | } 499 | 500 | static inline const byte *get_common_key_ctr(uint64_t titleid, 501 | bool is_retail) 502 | { 503 | static const byte ctr_ckeyX_dev[16] = { 504 | 0xbd, 0x4f, 0xe7, 0xe7, 0x33, 0xc7, 0x55, 0xfc, 0xe7, 0x54, 0x0e, 0xab, 0xbd, 0x8a, 0xc3, 0x0d 505 | }; 506 | static const byte ctr_ckeyX_retail[16] = { 507 | 0x61, 0x70, 0x85, 0x71, 0x9b, 0x7c, 0xfb, 0x31, 0x6d, 0xf4, 0xdf, 0x2e, 0x83, 0x62, 0xc6, 0xe2 508 | }; 509 | 510 | static byte ckey[16]; 511 | const byte *keyY; 512 | 513 | if ((keyY = get_common_keyY_ctr(titleid, is_retail)) == NULL) 514 | return NULL; 515 | 516 | return crypto_ctr_key_scramble(ckey, is_retail ? ctr_ckeyX_retail : ctr_ckeyX_dev, keyY); 517 | } 518 | 519 | static inline const byte *get_common_key(uint64_t titleid, bool is_retail) 520 | { 521 | static const byte wii_ckey_retail[16] = { 522 | 0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, 0x48, 0xd9, 0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7 523 | }; 524 | static const byte dsi_ckey_retail[16] = { 525 | 0xaf, 0x1b, 0xf5, 0x16, 0xa8, 0x07, 0xd2, 0x1a, 0xea, 0x45, 0x98, 0x4f, 0x04, 0x74, 0x28, 0x61 526 | }; 527 | static const byte wiiu_ckey_retail[16] = { 528 | 0xd7, 0xb0, 0x04, 0x02, 0x65, 0x9b, 0xa2, 0xab, 0xd2, 0xcb, 0x0d, 0xb2, 0x7f, 0xa2, 0xb6, 0x56 529 | }; 530 | static const byte wii_ckey_dev[16] = { 531 | 0xa1, 0x60, 0x4a, 0x6a, 0x71, 0x23, 0xb5, 0x29, 0xae, 0x8b, 0xec, 0x32, 0xc8, 0x16, 0xfc, 0xaa 532 | }; 533 | static const byte wiiu_ckey_dev[16] = { 534 | 0x2f, 0x5c, 0x1b, 0x29, 0x44, 0xe7, 0xfd, 0x6f, 0xc3, 0x97, 0x96, 0x4b, 0x05, 0x76, 0x91, 0xfa 535 | }; 536 | static const byte acer_ckey[16] = { 537 | 0xa2, 0xc8, 0x80, 0x41, 0xe2, 0x36, 0x73, 0x4b, 0x43, 0x6e, 0x76, 0x4b, 0xcb, 0x83, 0x0f, 0x61 538 | }; 539 | 540 | uint16_t platform = ((titleid >> 48) & 0xffff); 541 | if (platform == 0) platform = ((titleid >> 32) & 0xffff); 542 | 543 | switch (platform) { 544 | /* Wii */ 545 | case 0x0000: 546 | case 0x0001: 547 | return (is_retail ? wii_ckey_retail : wii_ckey_dev); 548 | /* iQue NetCard (unreleased) */ 549 | case 0x0002: 550 | return NULL; 551 | /* DSi */ 552 | case 0x0003: 553 | /* DSi and Wii share their dev common keys. */ 554 | return (is_retail ? dsi_ckey_retail : wii_ckey_dev); 555 | /* 3DS */ 556 | case 0x0004: 557 | return get_common_key_ctr(titleid, is_retail); 558 | /* Wii U */ 559 | case 0x0005: 560 | return (is_retail ? wiiu_ckey_retail : wiiu_ckey_dev); 561 | /* Acer */ 562 | case 0x0006: 563 | return acer_ckey; 564 | } 565 | 566 | return NULL; 567 | } 568 | 569 | static errno_t decrypt_titlekey(void) 570 | { 571 | gcry_error_t gerr; 572 | gcry_cipher_hd_t cipher; 573 | const byte *common_key; 574 | byte iv[16]; 575 | uint64_t bits; 576 | 577 | if ((gerr = gcry_cipher_open(&cipher, GCRY_CIPHER_AES128, 578 | GCRY_CIPHER_MODE_CBC, 0)) != 0) { 579 | err("Unable to open titlekey decryption context: %s", 580 | gcry_strerror(gerr)); 581 | return -1; 582 | } 583 | 584 | if ((common_key = get_common_key(opts.titleid, 585 | !(opts.flags & OPT_DEV_KEYS))) == NULL) { 586 | err("Error: Missing common key (unsupported/unknown platform)."); 587 | return -1; 588 | } 589 | 590 | if ((gerr = gcry_cipher_setkey(cipher, common_key, 16)) != 0) { 591 | err("Unable to set common key: %s", gcry_strerror(gerr)); 592 | gcry_cipher_close(cipher); 593 | return -1; 594 | } 595 | 596 | memset(iv, 0, sizeof(iv)); 597 | /* Title ID in big endian */ 598 | for (size_t i = 0; i < sizeof(opts.titleid); ++i) { 599 | bits = (8 * (sizeof(opts.titleid) - 1 - i)); 600 | iv[i] = (byte)((opts.titleid & (0xFFLLU << bits)) >> bits); 601 | } 602 | 603 | if ((gerr = gcry_cipher_setiv(cipher, iv, sizeof(iv))) != 0) { 604 | err("Unable to set IV: %s", gcry_strerror(gerr)); 605 | gcry_cipher_close(cipher); 606 | return -1; 607 | } 608 | 609 | if ((gerr = gcry_cipher_decrypt(cipher, opts.key, sizeof(opts.key), 610 | NULL, 0)) 611 | != 0) { 612 | err("Unable to decrypt titlekey: %s", gcry_strerror(gerr)); 613 | gcry_cipher_close(cipher); 614 | return -1; 615 | } 616 | 617 | gcry_cipher_close(cipher); 618 | 619 | opts.flags |= OPT_HAS_KEY; 620 | opts.flags &= ~OPT_KEY_ENCRYPTED; 621 | 622 | return 0; 623 | } 624 | 625 | static errno_t parse_cetk(void) 626 | { 627 | uint32_t sigtype = ((uint32_t)cetk[0] << 24) | ((uint32_t)cetk[1] << 16) 628 | | ((uint32_t)cetk[2] << 8) | cetk[3]; 629 | ssize_t offset; 630 | uint64_t titleid = 0; 631 | 632 | if ((offset = bytes_to_skip_for_signature(sigtype)) == -1) { 633 | err("Error: Unknown signature type %" PRIx32, sigtype); 634 | return -1; 635 | } 636 | 637 | /* Account for the initial four bytes used for the signature type. */ 638 | offset += 4; 639 | 640 | /* The Wii and DSi use earlier versions of the Ticket schema, but the 641 | * parts we care about are in the same positions. 642 | */ 643 | if (cetk[offset + 0x7c] > 0x1) { 644 | err("Warning: Unknown ticket version %" PRIu8 ", aborting.", 645 | cetk[offset + 0x7c]); 646 | return -1; 647 | } 648 | 649 | for (size_t i = 0; i < sizeof(titleid); ++i) 650 | titleid |= (uint64_t)(cetk[(size_t)offset + 0x9c + 651 | sizeof(titleid) - 1 - i]) << (8 * i); 652 | 653 | opts.titleid = titleid; 654 | memcpy(opts.key, cetk + offset + 0x7f, sizeof(opts.key)); 655 | 656 | common_key_index = cetk[offset + 0xb1]; 657 | 658 | return 0; 659 | } 660 | 661 | static size_t download_cetk_cb(char *ptr, size_t size, size_t nmemb, 662 | void *userdata) 663 | { 664 | byte *data = (byte *)ptr; 665 | size_t datalen = size * nmemb; 666 | 667 | (void)userdata; 668 | 669 | if (nmemb != 0 && datalen / nmemb != size) { 670 | err("Error: Overflow while calculating downloaded size."); 671 | return 0; 672 | } 673 | 674 | /* Only happens on empty file */ 675 | if (datalen == 0) { 676 | err("Error: Received 0-length cetk from remote.\n" 677 | "Does this title have a cetk?"); 678 | return 0; 679 | } 680 | 681 | if (cetk != NULL) { 682 | if (cetksize + datalen < cetksize) { 683 | err("Overflow while calculating downloaded size."); 684 | return 0; 685 | } 686 | 687 | if ((cetk = realloc(cetk, cetksize + datalen)) == NULL) 688 | oom(); 689 | 690 | memcpy(cetk + cetksize, data, datalen); 691 | 692 | cetksize += datalen; 693 | } else { 694 | if ((cetk = malloc(datalen)) == NULL) 695 | oom(); 696 | 697 | memcpy(cetk, data, datalen); 698 | cetksize = datalen; 699 | } 700 | 701 | return datalen; 702 | } 703 | 704 | static errno_t download_cetk(void) 705 | { 706 | struct DownloadState ds = {.flags = 0}; 707 | CURLcode code; 708 | long response_code; 709 | char *url; 710 | 711 | if ((url = get_cetk_url()) == NULL) 712 | oom(); 713 | 714 | if ((code = curl_easy_setopt(curl, CURLOPT_URL, url)) != CURLE_OK) { 715 | err("curl_easy_setopt(URL:cetk): %s", 716 | curlerrstr(code)); 717 | free(url); 718 | return -1; 719 | } 720 | 721 | if ((code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ds)) 722 | != CURLE_OK) { 723 | err("curl_easy_setopt(WRITEDATA): %s", 724 | curlerrstr(code)); 725 | free(url); 726 | return -1; 727 | } 728 | 729 | if ((code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, 730 | download_cetk_cb)) != CURLE_OK) { 731 | err("curl_easy_setopt(WRITEFUNCTION): %s", 732 | curlerrstr(code)); 733 | free(url); 734 | return -1; 735 | } 736 | 737 | *errbuf = '\0'; 738 | 739 | if (opts.flags & OPT_SHOW_PROGRESS) 740 | msg("Downloading CETK..."); 741 | 742 | if ((code = curl_easy_perform(curl)) != CURLE_OK) { 743 | free(url); 744 | 745 | /* We may be downloading a cetk due to -m, despite not wanting 746 | * to decrypt the title. However, this operation can, of course, 747 | * fail for titles that don't have cetk. 748 | * 749 | * Check if the failure is just a 404 (and not, say, a network 750 | * problem) and if we don't *need* the cetk. 751 | */ 752 | if ((code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, 753 | &response_code)) != CURLE_OK) { 754 | err("curl_easy_getinfo(cetk): %s", 755 | curlerrstr(code)); 756 | return -1; 757 | } 758 | 759 | if (response_code != 404 760 | || (opts.flags & OPT_DECRYPT_FROM_CETK)) { 761 | err("curl_easy_perform(cetk): %s", 762 | curlerrstr(code)); 763 | return -1; 764 | } 765 | 766 | /* If we did fail but fallthrough, the progress bar will 767 | * break the output on failure. 768 | */ 769 | if (opts.flags & OPT_SHOW_PROGRESS) 770 | printf("\n"); 771 | } else { /* Wrap free in else block to prevent doublefree on fallthrough. */ 772 | free(url); 773 | } 774 | 775 | if (ds.flags & DS_ERROR) 776 | return -1; 777 | 778 | return 0; 779 | } 780 | 781 | static errno_t download_contents_cb_simple_crypto( 782 | struct DownloadCryptoContext *dcc) 783 | { 784 | size_t overhang = 0; 785 | gcry_error_t gerr; 786 | struct DownloadState *ds = dcc->ds; 787 | 788 | /* CBC can only decrypt blockwise, but libcurl makes no promises as to 789 | * how much data we get, in particular, whether it's a multiple of 16 790 | * or not. 791 | */ 792 | static byte cbcbuf[16] = {0}; 793 | static size_t cbcbuflen = 0; 794 | 795 | /* If we are reading state from a partial file, do not re-apply 796 | * the decryption step (or we'll actually break the hash!), 797 | * only fill the hasher. 798 | * 799 | * However, because the CDN encryption uses AES in CBC mode, we need to 800 | * set the IV to match the state we've been downloading if we're 801 | * decrypting. 802 | * 803 | * In CBC decryption, the previous *ciphertext* is xored with the 804 | * current AES plaintext. Since we've written plaintext, we need to 805 | * re-encrypt everything up to this point to obtain the current IV. 806 | */ 807 | gcry_error_t (*cipher_func)(gcry_cipher_hd_t h, void *out, 808 | size_t outsize, const void *in, size_t inlen) = 809 | (ds->flags & DS_RESUMING 810 | ? gcry_cipher_encrypt 811 | : gcry_cipher_decrypt); 812 | 813 | /* If we have leftover block data: 814 | * - Append as much as is missing. 815 | * - Decrypt that. 816 | * - Write that first. 817 | * 818 | * This cannot happen when reading state back from a partially 819 | * downloaded file since we only write to file when we have a complete 820 | * AES block already. 821 | */ 822 | if (cbcbuflen != 0) { 823 | /* Do we have enough data to fill the cbcbuf? 824 | * If not, copy all data and continue next callback. 825 | */ 826 | if (cbcbuflen + dcc->datalen < sizeof(cbcbuf)) { 827 | memcpy(cbcbuf + cbcbuflen, dcc->data, dcc->datalen); 828 | cbcbuflen += dcc->datalen; 829 | 830 | return 0; 831 | } 832 | 833 | memcpy(cbcbuf + cbcbuflen, dcc->data, 16 - cbcbuflen); 834 | 835 | if ((gerr = gcry_cipher_decrypt(ds->cipher, cbcbuf, 836 | sizeof(cbcbuf), NULL, 0)) 837 | != 0) { 838 | err("Unable to decrypt content (%zu bytes): %s", 839 | 16 - cbcbuflen, gcry_strerror(gerr)); 840 | return -1; 841 | } 842 | 843 | fwrite(cbcbuf, sizeof(cbcbuf), 1, ds->f); 844 | 845 | /* On the Wii, at least 0001000248414241 has one content 846 | * (00000043) that has more data on the CDN than the TMD 847 | * indicates. 848 | * 849 | * Hash no more bytes than the TMD says, and discard 850 | * everything else. 851 | */ 852 | if (ds->bytes_hashed + sizeof(cbcbuf) > 853 | ds->content->size) { 854 | gcry_md_write(ds->hasher, cbcbuf, 855 | ds->content->size - ds->bytes_hashed); 856 | 857 | dcc->datalen = ds->content->size - ds->bytes_hashed; 858 | ds->bytes_hashed += dcc->datalen; 859 | } else { 860 | gcry_md_write(ds->hasher, cbcbuf, sizeof(cbcbuf)); 861 | ds->bytes_hashed += sizeof(cbcbuf); 862 | } 863 | 864 | dcc->data += 16 - cbcbuflen; 865 | dcc->datalen -= 16 - cbcbuflen; 866 | cbcbuflen = 0; 867 | } 868 | 869 | /* If we have data that's not aligned to a block, move the trailing 870 | * part to the cbcbuf. 871 | */ 872 | if ((overhang = dcc->datalen % 16) != 0) { 873 | memcpy(cbcbuf, dcc->data + dcc->datalen - overhang, overhang); 874 | 875 | dcc->datalen -= overhang; 876 | cbcbuflen = overhang; 877 | } 878 | 879 | /* We might just be getting the last CBC block, and that was 880 | * handled above. Check we still have things to do. 881 | */ 882 | if (dcc->datalen == 0) 883 | return 0; 884 | 885 | /* If we're downloading and decrypting, we need to *decrypt* in CBC 886 | * mode and feed the decrypted contents to the hasher. 887 | * 888 | * If we're resuming a download, we need to *encrypt* in CBC mode to 889 | * make sure we're continuing from the right IV, but that breaks 890 | * dcc->data being plaintext, so we need to hash first and then encrypt. 891 | */ 892 | #define HASH() do {\ 893 | if (ds->bytes_hashed + dcc->datalen > ds->content->size) {\ 894 | gcry_md_write(ds->hasher, dcc->data,\ 895 | ds->content->size - ds->bytes_hashed);\ 896 | dcc->datalen = ds->content->size - ds->bytes_hashed;\ 897 | ds->bytes_hashed += dcc->datalen;\ 898 | } else {\ 899 | gcry_md_write(ds->hasher, dcc->data, dcc->datalen);\ 900 | ds->bytes_hashed += dcc->datalen;\ 901 | }\ 902 | } while (0) 903 | #define CRYPT() do {\ 904 | if ((gerr = (*cipher_func)(ds->cipher,\ 905 | dcc->data, dcc->datalen,\ 906 | NULL, 0)) != 0) {\ 907 | err("Unable to crypt content (%zu bytes): %s",\ 908 | dcc->datalen, gcry_strerror(gerr));\ 909 | return -1;\ 910 | }\ 911 | } while (0) 912 | 913 | if (ds->flags & DS_RESUMING) { 914 | HASH(); 915 | CRYPT(); 916 | } else { 917 | CRYPT(); 918 | HASH(); 919 | } 920 | 921 | #undef HASH 922 | #undef CRYPT 923 | 924 | return 0; 925 | } 926 | 927 | static inline errno_t increase_hn_counter(struct HnCounters *count) 928 | { 929 | if (++count->h0 == 16) { 930 | count->h0 = 0; 931 | 932 | if (++count->h1 == 16) { 933 | count->h1 = 0; 934 | 935 | if (++count->h2 == 16) { 936 | err("Error: CDN content too long"); 937 | return -1; 938 | } 939 | } 940 | } 941 | 942 | return 0; 943 | } 944 | 945 | static errno_t decrypt_chunk(byte *chunk, struct DownloadState *ds) 946 | { 947 | gcry_error_t gerr; 948 | byte iv[16]; 949 | byte digest[0x20]; 950 | struct HnCounters *count = &ds->count; 951 | size_t digest_len = gcry_md_get_algo_dlen(ds->content->hashalgo); 952 | 953 | /* The first 0x400 bytes (meta information) are encrypted with 954 | * the usual CDN crypto. 955 | */ 956 | memset(iv, 0, sizeof(iv)); 957 | memcpy(iv, ds->content->idx, sizeof(ds->content->idx)); 958 | 959 | if ((gerr = gcry_cipher_setiv(ds->cipher, iv, sizeof(iv))) != 0) { 960 | err("Unable to set IV: %s", gcry_strerror(gerr)); 961 | return -1; 962 | } 963 | 964 | if ((gerr = gcry_cipher_decrypt(ds->cipher, chunk, CHUNK_HEADER_SIZE, 965 | NULL, 0)) != 0) { 966 | err("Unable to decrypt chunk header: %s", gcry_strerror(gerr)); 967 | return -1; 968 | } 969 | 970 | chunk[0] ^= ds->content->idx[0]; 971 | chunk[1] ^= ds->content->idx[1]; 972 | 973 | /* Verify H2 against H3 from the TMD. H2 cannot change because 974 | * H3 is fixated in the TMD. 975 | */ 976 | gcry_md_hash_buffer(ds->content->hashalgo, digest, chunk + 0x280, 977 | HN_SIZE); 978 | if (memcmp(digest, ds->content->h3, digest_len) != 0) { 979 | err("Error: H3 mismatch for content %08" PRIx32 ".", 980 | ds->content->contentid); 981 | return -1; 982 | } 983 | 984 | /* Verify H1 against current H2 */ 985 | gcry_md_hash_buffer(ds->content->hashalgo, digest, chunk + 0x140, 986 | HN_SIZE); 987 | if (memcmp(digest, chunk + 0x280 + (count->h2 * digest_len), digest_len) 988 | != 0) { 989 | err("Error: H2 mismatch for content %08" PRIx32 ".", 990 | ds->content->contentid); 991 | return -1; 992 | } 993 | 994 | /* Verify H0 against current H1 */ 995 | gcry_md_hash_buffer(ds->content->hashalgo, digest, chunk, HN_SIZE); 996 | 997 | if (memcmp(digest, chunk + 0x140 + (count->h1 * digest_len), digest_len) 998 | != 0) { 999 | err("Error: H1 mismatch for content %08" PRIx32 ".", 1000 | ds->content->contentid); 1001 | return -1; 1002 | } 1003 | 1004 | /* Decrypt the actual data */ 1005 | memcpy(iv, chunk + (count->h0 * digest_len), sizeof(iv)); 1006 | 1007 | if ((gerr = gcry_cipher_setiv(ds->cipher, iv, sizeof(iv))) != 0) { 1008 | err("Unable to set IV: %s", gcry_strerror(gerr)); 1009 | return -1; 1010 | } 1011 | 1012 | if ((gerr = gcry_cipher_decrypt(ds->cipher, chunk + CHUNK_HEADER_SIZE, 1013 | BLOCK_SIZE, NULL, 0)) 1014 | != 0) { 1015 | err("Unable to decrypt data block: %s", 1016 | gcry_strerror(gerr)); 1017 | return -1; 1018 | } 1019 | 1020 | /* Verify data against current H0 */ 1021 | gcry_md_hash_buffer(ds->content->hashalgo, digest, 1022 | chunk + CHUNK_HEADER_SIZE, BLOCK_SIZE); 1023 | 1024 | if (memcmp(digest, chunk + (count->h0 * digest_len), digest_len) != 0) { 1025 | err("Error: H0 mismatch for content %08" PRIx32 ".", 1026 | ds->content->contentid); 1027 | return -1; 1028 | } 1029 | 1030 | if (increase_hn_counter(count) != 0) 1031 | return -1; 1032 | 1033 | return 0; 1034 | } 1035 | 1036 | static errno_t download_contents_cb_blockwise_crypto( 1037 | struct DownloadCryptoContext *dcc) 1038 | { 1039 | size_t overhang; 1040 | 1041 | static byte chunk[CHUNK_SIZE]; 1042 | static size_t filled; 1043 | 1044 | if (dcc->datalen + filled < sizeof(chunk)) { 1045 | memcpy(chunk + filled, dcc->data, dcc->datalen); 1046 | filled += dcc->datalen; 1047 | return 0; 1048 | } 1049 | 1050 | /* We have more than or exactly enough data to fill the chunk. 1051 | * 1052 | * If resuming, we only need to bump the Hn counters, since the 1053 | * encryption is stateless between individual chunks. 1054 | */ 1055 | if (dcc->ds->flags & DS_RESUMING) { 1056 | increase_hn_counter(&dcc->ds->count); 1057 | return 0; 1058 | } 1059 | 1060 | overhang = dcc->datalen + filled - sizeof(chunk); 1061 | 1062 | memcpy(chunk + filled, dcc->data, dcc->datalen - overhang); 1063 | 1064 | if (decrypt_chunk(chunk, dcc->ds) != 0) 1065 | return -1; 1066 | 1067 | if (overhang > 0) 1068 | memcpy(chunk, dcc->data + dcc->datalen - overhang, overhang); 1069 | 1070 | filled = overhang; 1071 | dcc->datalen = 0; 1072 | 1073 | if (!(dcc->ds->flags & DS_RESUMING)) 1074 | fwrite(chunk, sizeof(chunk), 1, dcc->ds->f); 1075 | 1076 | return 0; 1077 | } 1078 | 1079 | static size_t download_contents_cb(char *ptr, size_t size, size_t nmemb, 1080 | void *userdata) 1081 | { 1082 | struct DownloadState *ds = userdata; 1083 | struct DownloadCryptoContext dcc; 1084 | byte *data = (byte *)ptr; 1085 | size_t datalen = size * nmemb; 1086 | size_t real_datalen = datalen; 1087 | 1088 | if (nmemb != 0 && datalen / nmemb != size) { 1089 | ds->flags |= DS_ERROR; 1090 | err("Error: Overflow while calculating downloaded size."); 1091 | return 0; 1092 | } 1093 | 1094 | /* Only happens on empty file */ 1095 | if (datalen == 0) { 1096 | ds->flags |= DS_ERROR; 1097 | err("Error: Received 0-length content from remote."); 1098 | return 0; 1099 | } 1100 | 1101 | if (opts.flags & OPT_HAS_KEY) { 1102 | dcc.data = data; 1103 | dcc.datalen = datalen; 1104 | dcc.ds = ds; 1105 | 1106 | if (has_simple_crypto(ds->content)) { 1107 | if (download_contents_cb_simple_crypto(&dcc) != 0) 1108 | return 0; 1109 | } else if (ds->content->type & TYPE_BLOCKWISECRYPTO) { 1110 | if (download_contents_cb_blockwise_crypto(&dcc) != 0) 1111 | return 0; 1112 | 1113 | return real_datalen; 1114 | } else { 1115 | /* XXX: Assuming there's always encryption. 1116 | * 1117 | * The CDN seems to really only operate with encrypted 1118 | * titles, though. 1119 | * 1120 | * This *may* be different for dev CDN(s), but no such 1121 | * thing has been seen in the wild thus far. 1122 | */ 1123 | err("Error: Unknown encryption for title."); 1124 | return 0; 1125 | } 1126 | 1127 | datalen = dcc.datalen; 1128 | data = dcc.data; 1129 | } 1130 | 1131 | /* We might just be getting the last CBC block, and that was 1132 | * handled above. Check we still have things to do. 1133 | */ 1134 | if (datalen > 0 && !(ds->flags & DS_RESUMING)) 1135 | fwrite(data, datalen, 1, ds->f); 1136 | 1137 | if (ferror(ds->f)) { 1138 | err("I/O error when reading/writing file"); 1139 | /* Signals an error to libcurl and aborts the transfer. */ 1140 | return 0; 1141 | } 1142 | 1143 | return real_datalen; 1144 | } 1145 | 1146 | static inline int get_hash_algo_from_tid(uint64_t titleid) 1147 | { 1148 | uint16_t platform = ((titleid >> 48) & 0xffff); 1149 | if (platform == 0) platform = ((titleid >> 32) & 0xffff); 1150 | 1151 | switch (platform) { 1152 | /* Wii */ 1153 | case 0x0000: 1154 | case 0x0001: 1155 | return GCRY_MD_SHA1; 1156 | /* iQue NetCard, unreleased */ 1157 | case 0x0002: 1158 | return GCRY_MD_SHA1; 1159 | /* DSi */ 1160 | case 0x0003: 1161 | return GCRY_MD_SHA1; 1162 | /* 3DS */ 1163 | case 0x0004: 1164 | return GCRY_MD_SHA256; 1165 | /* Wii U */ 1166 | case 0x0005: 1167 | /* What the fuck? */ 1168 | return GCRY_MD_SHA1; 1169 | /* Acer */ 1170 | case 0x0006: 1171 | return GCRY_MD_SHA256; 1172 | } 1173 | 1174 | return -1; 1175 | } 1176 | 1177 | static size_t download_and_verify_h3_cb(char *ptr, size_t size, size_t nmemb, 1178 | void *userdata) 1179 | { 1180 | struct Content *content = userdata; 1181 | size_t datalen = size * nmemb; 1182 | 1183 | if (nmemb != 0 && datalen / nmemb != size) { 1184 | err("Error: Overflow while calculating downloaded size."); 1185 | return 0; 1186 | } 1187 | 1188 | /* Only happens on empty file */ 1189 | if (datalen == 0) { 1190 | err("Error: Received 0-length content from remote."); 1191 | return 0; 1192 | } 1193 | 1194 | if (datalen > sizeof(content->h3)) { 1195 | err("Error: Received oversized H3, expected max 0x%zx bytes.", 1196 | sizeof(content->h3)); 1197 | return 0; 1198 | } 1199 | 1200 | memcpy(content->h3, ptr, datalen); 1201 | 1202 | return datalen; 1203 | } 1204 | 1205 | static errno_t download_and_verify_h3(struct Content *content) 1206 | { 1207 | CURLcode code; 1208 | byte digest[0x20]; 1209 | char *url; 1210 | 1211 | if ((code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, content)) 1212 | != CURLE_OK) { 1213 | err("curl_easy_setopt(WRITEDATA): %s", 1214 | curlerrstr(code)); 1215 | return -1; 1216 | } 1217 | 1218 | if ((code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, 1219 | download_and_verify_h3_cb)) 1220 | != CURLE_OK) { 1221 | err("curl_easy_setopt(WRITEFUNCTION): %s", 1222 | curlerrstr(code)); 1223 | return -1; 1224 | } 1225 | 1226 | if ((url = get_h3_url_for_content(content)) == NULL) 1227 | oom(); 1228 | 1229 | if ((code = curl_easy_setopt(curl, CURLOPT_URL, url)) != CURLE_OK) { 1230 | err("curl_easy_setopt(URL:%08" PRIx32 ".h3): %s", 1231 | content->contentid, curlerrstr(code)); 1232 | free(url); 1233 | return -1; 1234 | } 1235 | 1236 | if (opts.flags & OPT_SHOW_PROGRESS) 1237 | msg("Downloading H3 for content %08" PRIx32 "...", 1238 | content->contentid); 1239 | 1240 | *errbuf = '\0'; 1241 | 1242 | if ((code = curl_easy_perform(curl)) != CURLE_OK) { 1243 | err("curl_easy_perform(%08" PRIx32 ".h3): %s", 1244 | content->contentid, curlerrstr(code)); 1245 | free(url); 1246 | return -1; 1247 | } 1248 | 1249 | free(url); 1250 | 1251 | gcry_md_hash_buffer(content->hashalgo, digest, content->h3, 1252 | gcry_md_get_algo_dlen(content->hashalgo)); 1253 | 1254 | if (memcmp(content->hash, digest, 1255 | gcry_md_get_algo_dlen(content->hashalgo)) 1256 | != 0) { 1257 | err("Error: Hash mismatch for H3 of content " 1258 | "%08" PRIx32, 1259 | content->contentid); 1260 | return -1; 1261 | } 1262 | 1263 | return 0; 1264 | } 1265 | 1266 | static errno_t prepare_crypto_for_download(struct DownloadState *ds) 1267 | { 1268 | gcry_error_t gerr; 1269 | byte iv[16] = {0}; 1270 | 1271 | if (has_simple_crypto(ds->content)) { 1272 | memcpy(iv, ds->content->idx, sizeof(ds->content->idx)); 1273 | 1274 | if ((gerr = gcry_cipher_setiv(ds->cipher, iv, sizeof(iv))) 1275 | != 0) { 1276 | err("Unable to set IV: %s", 1277 | gcry_strerror(gerr)); 1278 | return -1; 1279 | } 1280 | } else if (ds->content->type & TYPE_BLOCKWISECRYPTO) { 1281 | if (download_and_verify_h3(ds->content) != 0) 1282 | return -1; 1283 | } 1284 | 1285 | return 0; 1286 | } 1287 | 1288 | /* Side effect: Moves the FILE* to the end of file so that appending works 1289 | * transparently as though a new download had begun. 1290 | */ 1291 | static errno_t read_partial_file_into_state(struct DownloadState *ds) 1292 | { 1293 | CURLcode code; 1294 | /* Avoids having to deal with partial chunks for contents using 1295 | * blockwise crypto. 1296 | */ 1297 | static byte buf[CHUNK_SIZE]; 1298 | size_t bytes_read; 1299 | size_t filelen; 1300 | char filename[9]; 1301 | char *filepath; 1302 | 1303 | snprintf(filename, sizeof(filename), "%08x", ds->content->contentid); 1304 | filepath = util_get_filepath(filename); 1305 | 1306 | /* File not being found is *not* okay -- we may have just created it 1307 | * with the fopen(..., "w+b") call in download_content(). 1308 | * 1309 | * File being empty, however, is. 1310 | */ 1311 | if (util_get_file_size(filepath, &filelen) != 0) 1312 | return -1; 1313 | 1314 | if (filelen == 0) 1315 | return 0; 1316 | 1317 | if (filelen > ds->content->size) { 1318 | err("Content size (%" PRIu64 ") is greater than size of" 1319 | " local file (%08" PRIx32 ": %" PRIu64 "." 1320 | " Broken resume; refusing to continue.", 1321 | ds->content->size, ds->content->contentid, 1322 | filelen); 1323 | return -1; 1324 | } 1325 | 1326 | if (filelen == ds->content->size && (opts.flags & OPT_SHOW_PROGRESS)) { 1327 | msg("Skipping already downloaded content %08" PRIx32 ".", 1328 | ds->content->contentid); 1329 | return 1; 1330 | } 1331 | 1332 | ds->flags |= DS_RESUMING; 1333 | 1334 | while ((bytes_read = fread(buf, 1, sizeof(buf), ds->f)) != 0) { 1335 | /* CURL uses a different return code convention, where a return 1336 | * value of less than the total amount of data the function was 1337 | * called with signifies an error. 1338 | * 1339 | * Since we're using our internal download callback function, 1340 | * expect this kind of return value here. 1341 | */ 1342 | if (download_contents_cb((char *)buf, 1, bytes_read, ds) 1343 | != bytes_read) 1344 | return -1; 1345 | } 1346 | 1347 | if (ferror(ds->f)) { 1348 | err("Reading file failed: %s", strerror(errno)); 1349 | return -1; 1350 | } 1351 | 1352 | ds->flags &= ~DS_RESUMING; 1353 | 1354 | /* Guard against libcurl compiled with < 64-bit curl_off_t if the input 1355 | * is beyond that size. 1356 | * 1357 | * curl_off_t is a signed type and overflowing that is undefined 1358 | * behavior. 1359 | * 1360 | * XXX: We assume in the entire program, top to bottom, that the program 1361 | * is running on a machine CHAR_BIT == 8. 1362 | * 1363 | * There's no macro for the maximum value provided by libcurl, so we'll 1364 | * have to work with the size the type (which *is* provided). 8 times 1365 | * the size of the type yields the number of bits in the type. We need 1366 | * to subtract 2 from that; one because 1 << bit_size is always an 1367 | * overflow and another because 1 << (bit_size - 1) is an overflow on 1368 | * signed integers, and curl_off_t is a signed type. 1369 | */ 1370 | if (8 * CURL_SIZEOF_CURL_OFF_T - 2 <= util_get_msb64(filelen)) { 1371 | err("File %08" PRIx32 " too large for your platform to resume.", 1372 | ds->content->contentid); 1373 | return -1; 1374 | } 1375 | 1376 | if ((code = curl_easy_setopt(curl, CURLOPT_RESUME_FROM_LARGE, 1377 | (curl_off_t)filelen)) != CURLE_OK) { 1378 | err("curl_easy_setopt(URL:%08" PRIx32 "): %s", 1379 | ds->content->contentid, curlerrstr(code)); 1380 | return -1; 1381 | } 1382 | 1383 | if (opts.flags & OPT_SHOW_PROGRESS) 1384 | msg("Resuming download from byte %" PRIu64 ".", filelen); 1385 | 1386 | return 0; 1387 | } 1388 | 1389 | static errno_t download_content(struct DownloadState *ds) 1390 | { 1391 | FILE *f; 1392 | uint64_t filelen; 1393 | char filename[9]; 1394 | CURLcode code; 1395 | char *url; 1396 | char *filepath; 1397 | 1398 | snprintf(filename, sizeof(filename), "%08x", ds->content->contentid); 1399 | 1400 | filepath = util_get_filepath(filename); 1401 | 1402 | errno = 0; 1403 | /* Since r+b won't create the file, but we need the file pointer at the 1404 | * beginning of the file, we'll just manually create the file. 1405 | */ 1406 | if (util_create_file(filepath) != 0) { 1407 | err("Unable to create file %s: %s", filepath, strerror(errno)); 1408 | free(filepath); 1409 | return -1; 1410 | } 1411 | 1412 | errno = 0; 1413 | if ((f = fopen(filepath, "r+b")) == NULL) { 1414 | err("Unable to open %s for reading and writing: %s", filepath, 1415 | strerror(errno)); 1416 | free(filepath); 1417 | return -1; 1418 | } 1419 | 1420 | ds->f = f; 1421 | 1422 | if (opts.flags & OPT_HAS_KEY) { 1423 | if (prepare_crypto_for_download(ds) != 0) { 1424 | free(filepath); 1425 | return -1; 1426 | } 1427 | } 1428 | 1429 | if ((url = get_content_url(ds->content)) == NULL) { 1430 | free(filepath); 1431 | oom(); 1432 | } 1433 | 1434 | if ((code = curl_easy_setopt(curl, CURLOPT_URL, url)) != CURLE_OK) { 1435 | err("curl_easy_setopt(URL:%08" PRIx32 "): %s", 1436 | ds->content->contentid, curlerrstr(code)); 1437 | free(url); 1438 | free(filepath); 1439 | return -1; 1440 | } 1441 | 1442 | if ((code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, ds)) 1443 | != CURLE_OK) { 1444 | err("curl_easy_setopt(WRITEDATA): %s", 1445 | curlerrstr(code)); 1446 | free(url); 1447 | fclose(f); 1448 | free(filepath); 1449 | return -1; 1450 | } 1451 | 1452 | if ((code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, 1453 | download_contents_cb)) 1454 | != CURLE_OK) { 1455 | err("curl_easy_setopt(WRITEFUNCTION): %s", 1456 | curlerrstr(code)); 1457 | free(url); 1458 | fclose(f); 1459 | free(filepath); 1460 | return -1; 1461 | } 1462 | 1463 | if (opts.flags & OPT_SHOW_PROGRESS) 1464 | msg("Downloading content %08" PRIx32 "...", 1465 | ds->content->contentid); 1466 | 1467 | /* Restoring download state from file relies on the order of downloading 1468 | * being deterministic and no downloads being done in parallel. 1469 | * 1470 | * Else, this would require a separate loop inside download_contents. 1471 | */ 1472 | if (opts.flags & OPT_RESUME) { 1473 | switch (read_partial_file_into_state(ds)) { 1474 | /* Download already complete */ 1475 | case 1: 1476 | free(filepath); 1477 | return 0; 1478 | 1479 | /* File partially downloaded; state has been read 1480 | * successfully. 1481 | */ 1482 | case 0: 1483 | break; 1484 | 1485 | /* Some operation failed. */ 1486 | default: 1487 | free(filepath); 1488 | return -1; 1489 | } 1490 | } 1491 | 1492 | *errbuf = '\0'; 1493 | 1494 | if ((code = curl_easy_perform(curl)) != CURLE_OK) { 1495 | err("curl_easy_perform(%08" PRIx32 "): %s", 1496 | ds->content->contentid, curlerrstr(code)); 1497 | free(url); 1498 | fclose(f); 1499 | free(filepath); 1500 | return -1; 1501 | } 1502 | 1503 | free(url); 1504 | 1505 | /* The number of bytes to resume from persists between individual 1506 | * URLs; we need to reset it to 0 after a completed transfer. 1507 | */ 1508 | if ((code = curl_easy_setopt(curl, CURLOPT_RESUME_FROM, 0)) 1509 | != CURLE_OK) { 1510 | err("curl_easy_setopt(URL:%08" PRIx32 "): %s", 1511 | ds->content->contentid, curlerrstr(code)); 1512 | free(filepath); 1513 | return -1; 1514 | } 1515 | 1516 | fclose(f); 1517 | 1518 | if (util_get_file_size(filepath, &filelen) == 0 1519 | && filelen != ds->content->size) { 1520 | err("Warning: File size mismatch (got %" PRIu64 1521 | " vs. expected %" PRIu64 1522 | ") for content %08" PRIx32, 1523 | filelen, 1524 | ds->content->size, 1525 | ds->content->contentid); 1526 | } 1527 | 1528 | if (ds->flags & DS_ERROR) { 1529 | free(filepath); 1530 | return -1; 1531 | } 1532 | 1533 | if (!(opts.flags & OPT_HAS_KEY)) { 1534 | free(filepath); 1535 | return 0; 1536 | } 1537 | 1538 | /* TMD SHA-1/hash tree-based verification happened as part of the 1539 | * decryption for blockwise crypto contents. 1540 | */ 1541 | if (has_simple_crypto(ds->content)) { 1542 | gcry_md_final(ds->hasher); 1543 | 1544 | if (memcmp(gcry_md_read(ds->hasher, ds->content->hashalgo), 1545 | ds->content->hash, 1546 | gcry_md_get_algo_dlen(ds->content->hashalgo)) 1547 | != 0) { 1548 | err("Error: Hash mismatch for content %08" PRIx32, 1549 | ds->content->contentid); 1550 | free(filepath); 1551 | return -1; 1552 | } 1553 | } 1554 | 1555 | gcry_cipher_reset(ds->cipher); 1556 | gcry_md_reset(ds->hasher); 1557 | 1558 | free(filepath); 1559 | return 0; 1560 | } 1561 | 1562 | static errno_t download_contents(void) 1563 | { 1564 | struct DownloadState ds; 1565 | int hashalgo = 0; 1566 | gcry_error_t gerr; 1567 | errno_t ret = 0; 1568 | 1569 | memset(&ds, 0, sizeof(ds)); 1570 | 1571 | if (opts.flags & OPT_HAS_KEY) { 1572 | if ((gerr = gcry_cipher_open(&ds.cipher, GCRY_CIPHER_AES128, 1573 | GCRY_CIPHER_MODE_CBC, 0)) 1574 | != 0) { 1575 | err("Unable to open decryption context: %s", 1576 | gcry_strerror(gerr)); 1577 | return -1; 1578 | } 1579 | 1580 | if ((gerr = gcry_cipher_setkey(ds.cipher, opts.key, 1581 | sizeof(opts.key))) != 0) { 1582 | err("Unable to set key: %s", 1583 | gcry_strerror(gerr)); 1584 | gcry_cipher_close(ds.cipher); 1585 | return -1; 1586 | } 1587 | 1588 | if ((hashalgo = get_hash_algo_from_tid(opts.titleid)) == -1) { 1589 | err("Unknown platform for title ID %016" PRIu64, 1590 | opts.titleid); 1591 | gcry_cipher_close(ds.cipher); 1592 | return -1; 1593 | } 1594 | 1595 | if ((gerr = gcry_md_open(&ds.hasher, hashalgo, 0)) 1596 | != 0) { 1597 | err("Unable to open hasher: %s", 1598 | gcry_strerror(gerr)); 1599 | gcry_cipher_close(ds.cipher); 1600 | return -1; 1601 | } 1602 | } 1603 | 1604 | for (struct Content *content = contents; 1605 | content != NULL; 1606 | content = content->next) { 1607 | ds.content = content; 1608 | ds.bytes_hashed = 0; 1609 | memset(&ds.count, 0, sizeof(ds.count)); 1610 | content->hashalgo = hashalgo; 1611 | 1612 | if ((ret = download_content(&ds)) != 0) 1613 | break; 1614 | } 1615 | 1616 | if (opts.flags & OPT_HAS_KEY) { 1617 | gcry_cipher_close(ds.cipher); 1618 | gcry_md_close(ds.hasher); 1619 | } 1620 | 1621 | return ret; 1622 | } 1623 | 1624 | static void free_contents_list() 1625 | { 1626 | struct Content *cur, *next; 1627 | 1628 | for (cur = contents, next = cur->next; cur != NULL; cur = next, next = cur ? cur->next : NULL) { 1629 | free(cur); 1630 | } 1631 | } 1632 | static void download_fini(void) 1633 | { 1634 | free_contents_list(); 1635 | curl_easy_cleanup(curl); 1636 | } 1637 | 1638 | errno_t download_title(void) 1639 | { 1640 | errno_t ret; 1641 | 1642 | if ((ret = download_init()) != 0) 1643 | return ret; 1644 | 1645 | /* The -r option does *not* prevent redownloading tmd/cetk. Those are 1646 | * small enough to fetch anew. 1647 | */ 1648 | 1649 | if ((ret = download_tmd()) != 0) 1650 | return ret; 1651 | 1652 | if ((opts.flags & OPT_DECRYPT_FROM_CETK) 1653 | || (opts.flags & OPT_KEEP_META)) { 1654 | if ((ret = download_cetk()) != 0) 1655 | return ret; 1656 | } 1657 | 1658 | if (opts.flags & OPT_DECRYPT_FROM_CETK) { 1659 | if ((ret = parse_cetk()) != 0) 1660 | return -1; 1661 | } 1662 | 1663 | if (opts.flags & OPT_KEEP_META) { 1664 | if ((ret = write_file_from_memory("tmd", tmd, tmdsize)) != 0) 1665 | return ret; 1666 | 1667 | if ((ret = write_file_from_memory("cetk", cetk, cetksize)) != 0) 1668 | return ret; 1669 | } 1670 | 1671 | if (opts.flags & OPT_KEY_ENCRYPTED) { 1672 | if ((ret = decrypt_titlekey()) != 0) 1673 | return ret; 1674 | } 1675 | 1676 | if ((ret = download_contents()) != 0) 1677 | return ret; 1678 | 1679 | download_fini(); 1680 | 1681 | return 0; 1682 | } 1683 | 1684 | -------------------------------------------------------------------------------- /download.h: -------------------------------------------------------------------------------- 1 | #ifndef NUSTOOL_DOWNLOAD_H 2 | #define NUSTOOL_DOWNLOAD_H 3 | 4 | #include 5 | 6 | #include "crypto.h" 7 | #include "types.h" 8 | 9 | #define DEFAULT_NUS_BASE_URL "http://ccs.cdn.c.shop.nintendowifi.net/ccs/download" 10 | 11 | #define DS_ERROR (1UL << 31) 12 | /* Do not write to file; switch CBC mode to encrypt. Used to restore state from 13 | * an aborted download. 14 | */ 15 | #define DS_RESUMING (1UL << 30) 16 | 17 | #define TYPE_ENCRYPTED (1U << 0) 18 | /* aka type "disc" */ 19 | #define TYPE_BLOCKWISECRYPTO (1U << 1) 20 | #define TYPE_CFM (1U << 2) 21 | /* Flag 0x2000 unknown. */ 22 | #define TYPE_OPTIONAL (1U << 14) 23 | #define TYPE_SHARED (1U << 15) 24 | 25 | #define CHUNK_SIZE 0x10000U 26 | #define BLOCK_SIZE 0x0fc00U 27 | #define CHUNK_HEADER_SIZE 0x00400U 28 | #define HN_SIZE 0x140U 29 | 30 | /* TMD content */ 31 | struct Content { 32 | struct Content *next; 33 | 34 | uint32_t contentid; 35 | /* We only use this for the IV for decryption anyway, so there's no 36 | * use storing this as a uint16_t. 37 | */ 38 | byte idx[2]; 39 | uint16_t type; 40 | uint64_t size; 41 | int hashalgo; 42 | /* Holds a SHA-1 hash with 0-padding. 43 | * On 3DS, holds a SHA-256 hash. 44 | */ 45 | byte hash[0x20]; 46 | /* Used for blockwise crypto: Top level hash of this hash tree. */ 47 | byte h3[0x20]; 48 | }; 49 | 50 | struct HnCounters { 51 | size_t h0; 52 | size_t h1; 53 | size_t h2; 54 | }; 55 | 56 | struct DownloadState { 57 | unsigned long flags; 58 | struct Content *content; 59 | FILE *f; 60 | 61 | size_t bytes_hashed; 62 | struct HnCounters count; 63 | gcry_cipher_hd_t cipher; 64 | gcry_md_hd_t hasher; 65 | }; 66 | 67 | struct DownloadCryptoContext { 68 | unsigned char *data; 69 | size_t datalen; 70 | struct DownloadState *ds; 71 | }; 72 | 73 | enum SignatureTypeID { 74 | SIG_UNDERFLOW = 0x00FFFF, 75 | RSA_4096_SHA_1 = 0x010000, 76 | RSA_2048_SHA_1 = 0x010001, 77 | EC_SHA_1 = 0x010002, 78 | RSA_4096_SHA_256 = 0x010003, 79 | RSA_2048_SHA_256 = 0x010004, 80 | EC_SHA_256 = 0x010005, 81 | SIG_OVERFLOW = 0x010006 82 | }; 83 | 84 | struct TMDFileFormat { 85 | size_t ncontents_offset; 86 | size_t contents_offset; 87 | size_t title_version_offset; 88 | 89 | size_t content_chunk_len; 90 | size_t hash_len; 91 | }; 92 | 93 | static inline bool has_simple_crypto(const struct Content *content) 94 | { 95 | return ((content->type & TYPE_ENCRYPTED) 96 | && !(content->type & TYPE_BLOCKWISECRYPTO)); 97 | } 98 | 99 | errno_t download_title(void); 100 | 101 | #endif 102 | 103 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "crypto.h" 4 | #include "main.h" 5 | #include "download.h" 6 | #include "types.h" 7 | #include "util.h" 8 | 9 | struct Options opts; 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | errno_t ret; 14 | 15 | if (gcry_check_version("1.5.0") == NULL) { 16 | err("Your libgcrypt is too old. Required version: >= 1.5.0"); 17 | return EXIT_FAILURE; 18 | } 19 | 20 | /* We don't deal with sensitive keys here. */ 21 | if (gcry_control(GCRYCTL_DISABLE_SECMEM, 0) != 0) { 22 | err("Unable to disable gcrypt paranoia."); 23 | return EXIT_FAILURE; 24 | } 25 | 26 | if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0) != 0) { 27 | err("Unable to finish gcrypt initialization."); 28 | return EXIT_FAILURE; 29 | } 30 | 31 | if ((ret = util_parse_options(argc, argv)) != 0) 32 | return (ret < 0) ? EXIT_FAILURE : EXIT_SUCCESS; 33 | 34 | if (download_title() != 0) 35 | return EXIT_FAILURE; 36 | 37 | return EXIT_SUCCESS; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | #ifndef NUSTOOL_MAIN_H 2 | #define NUSTOOL_MAIN_H 3 | 4 | #include "types.h" 5 | 6 | #define OPT_SHOW_PROGRESS (1UL << 0) 7 | #define OPT_HAS_KEY (1UL << 1) 8 | #define OPT_HAS_VERSION (1UL << 2) 9 | #define OPT_DECRYPT_FROM_CETK (1UL << 3) 10 | #define OPT_KEY_ENCRYPTED (1UL << 4) 11 | #define OPT_KEEP_META (1UL << 5) 12 | #define OPT_RESUME (1UL << 6) 13 | #define OPT_DEV_KEYS (1UL << 7) 14 | #define OPT_LOCAL_FILES (1UL << 8) 15 | 16 | struct Options { 17 | uint64_t titleid; 18 | byte key[16]; 19 | unsigned long flags; 20 | uint16_t version; 21 | }; 22 | 23 | extern struct Options opts; 24 | 25 | #endif 26 | 27 | -------------------------------------------------------------------------------- /types.h: -------------------------------------------------------------------------------- 1 | #ifndef NUSTOOL_TYPES_H 2 | #define NUSTOOL_TYPES_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef uint8_t byte; 10 | 11 | #ifndef __STDC_LIB_EXT1__ 12 | typedef int errno_t; 13 | #endif 14 | 15 | #endif 16 | 17 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "main.h" 13 | #include "types.h" 14 | #include "util.h" 15 | #include "version.h" 16 | 17 | #ifdef _WIN32 18 | #define MAKEDIR(A, B) _mkdir(A) 19 | #else 20 | #define MAKEDIR(A, B) mkdir(A, B) 21 | #endif 22 | 23 | __attribute__((noreturn)) void oom(void) 24 | { 25 | fputs("Out of memory.", stderr); 26 | exit(EXIT_FAILURE); 27 | } 28 | 29 | __attribute__((format(printf, 1, 2))) void err(const char *fmt, ...) 30 | { 31 | va_list ap; 32 | 33 | va_start(ap, fmt); 34 | vfprintf(stderr, fmt, ap); 35 | va_end(ap); 36 | 37 | fputc('\n', stderr); 38 | } 39 | 40 | __attribute__((format(printf, 1, 2))) void msg(const char *fmt, ...) 41 | { 42 | va_list ap; 43 | 44 | va_start(ap, fmt); 45 | vfprintf(stdout, fmt, ap); 46 | va_end(ap); 47 | 48 | fputc('\n', stdout); 49 | } 50 | 51 | static void usage(const char *name) 52 | { 53 | err("Usage: %s [-cDmpr] [-k decrypted_key] [-K encrypted_key]\n" 54 | " [-V version] titleid", 55 | /* The C standard does not guarantee that argv[0] is non-NULL, but does 56 | * guarantee that argv[argc] is NULL. 57 | * 58 | * Similarly, passing NULL as %s is undefined behavior, so we try to 59 | * work around these odd conditions. 60 | */ 61 | name ? name : "(?)"); 62 | } 63 | 64 | static void help(const char *name) 65 | { 66 | usage(name); 67 | err("\nDownloads and optionally decrypts a title from NUS.\n" 68 | "\n" 69 | " -c try to decrypt the title using the CETK key\n" 70 | " -D if decrypting the title using the CETK key, use the\n" 71 | " development common key to decrypt the titlekey\n" 72 | " -k [key] the titlekey to use to decrypt the contents\n" 73 | " -K [key] the encrypted titlekey to use to decrypt the contents\n" 74 | " -h print this help and exit\n" 75 | " -m keep meta files (cetk, tmd); usable with make_cdn_cia\n" 76 | " -l download files to local directory instead of tid/ver/\n" 77 | " -p show progress bars\n" 78 | " -r resume download\n" 79 | " -v print nustool version and exit\n" 80 | " -V [version] the version of the title to download; if not given,\n" 81 | " the latest version will be downloaded\n" 82 | "\n" 83 | "If none of -c, -D, -k and -K are given, the raw encrypted contents\n" 84 | "will be downloaded.\n" 85 | "\n" 86 | "All files are downloaded into a titleid/version/ directory."); 87 | } 88 | 89 | static void version(void) 90 | { 91 | msg("nustool version %s", NUSTOOL_VERSION); 92 | } 93 | 94 | static bool is_valid_hex_char(int c) 95 | { 96 | return ( 97 | (c >= 'A' && c <= 'F') || 98 | (c >= 'a' && c <= 'f') || 99 | (c >= '0' && c <= '9') 100 | ); 101 | } 102 | 103 | static uint8_t get_hex_char_value(int c) 104 | { 105 | if (c >= 'A' && c <= 'F') { 106 | return (uint8_t)((c - 'A') + 0xA); 107 | } else if (c >= 'a' && c <= 'f') { 108 | return (uint8_t)((c - 'a') + 0xA); 109 | } else { 110 | return (uint8_t)(c - '0'); 111 | } 112 | } 113 | 114 | static void strip_spaces(char *in) { 115 | char *src = in, *dst = in; 116 | 117 | while(*src) { 118 | if(*src == ' ') 119 | src++; 120 | else 121 | *dst++ = *src++; 122 | } 123 | 124 | *dst = '\0'; 125 | } 126 | 127 | errno_t util_parse_hex(char *in, byte *out, size_t outlen) 128 | { 129 | size_t inlen; 130 | 131 | strip_spaces(in); 132 | 133 | if ((inlen = strlen(in)) & 1) 134 | return -1; 135 | if (outlen < inlen / 2) 136 | return -1; 137 | 138 | for (size_t i = 0; i < inlen; i += 2) { 139 | if (!is_valid_hex_char(in[i]) || !is_valid_hex_char(in[i + 1])) 140 | return -1; 141 | 142 | out[i / 2] = (byte)((get_hex_char_value(in[i]) << 4) | 143 | get_hex_char_value(in[i + 1])); 144 | } 145 | 146 | return 0; 147 | } 148 | 149 | errno_t util_create_outdir(void) 150 | { 151 | if (opts.flags & OPT_LOCAL_FILES) 152 | return 0; 153 | 154 | char *dir = calloc(MAX_FILEPATH_LEN, sizeof(char)); 155 | 156 | snprintf(dir, MAX_FILEPATH_LEN, "%016" PRIx64, opts.titleid); 157 | if (MAKEDIR(dir, 0777) == -1 && errno != EEXIST) { 158 | free(dir); 159 | return -1; 160 | } 161 | 162 | snprintf(dir + strlen(dir), MAX_FILEPATH_LEN - strlen(dir), "/%" PRIu16, opts.version); 163 | if (MAKEDIR(dir, 0777) == -1 && errno != EEXIST) { 164 | free(dir); 165 | return -1; 166 | } 167 | 168 | free(dir); 169 | return 0; 170 | } 171 | 172 | char *util_get_filepath(const char *path) 173 | { 174 | char *filepath = calloc(MAX_FILEPATH_LEN, sizeof(char)); 175 | 176 | if (!(opts.flags & OPT_LOCAL_FILES)) 177 | snprintf(filepath, MAX_FILEPATH_LEN - MAX_FILENAME_LEN, "%016" PRIx64 "/%" PRIu16 "/", opts.titleid, opts.version); 178 | snprintf(filepath + strlen(filepath), MAX_FILENAME_LEN, "%s", path); 179 | 180 | return filepath; 181 | } 182 | 183 | const char *util_print_hex(const byte bytes[], size_t length, char *out) 184 | { 185 | static const char *hex_str = "0123456789abcdef"; 186 | 187 | for (size_t i = 0; i < length; ++i) { 188 | out[2*i] = hex_str[bytes[i] >> 4]; 189 | out[2*i + 1] = hex_str[bytes[i] & 0xF]; 190 | } 191 | 192 | out[2 * length] = 0; 193 | 194 | return out; 195 | } 196 | 197 | static errno_t util_parse_num(const char *str, char flag, 198 | uint64_t *num, int base, 199 | uint64_t min, uint64_t max) 200 | { 201 | char *end; 202 | 203 | errno = 0; 204 | *num = strtoull(str, &end, base); 205 | 206 | if (*str == '\0') { 207 | if (flag == 0) 208 | err("No argument given."); 209 | else 210 | err("No argument given for flag %c.", flag); 211 | 212 | return -1; 213 | } 214 | 215 | if (*end != '\0') { 216 | err("%s is not a valid number.", str); 217 | return -1; 218 | } 219 | 220 | if (errno == ERANGE && *num == ULONG_MAX) { 221 | err("%s is out of this machine's range (max: %llx).", 222 | str, ULLONG_MAX); 223 | return -1; 224 | } 225 | 226 | if (*num < min || *num > max) { 227 | err("%s is out of range (range: 0x%llx-0x%llx).", str, min, max); 228 | return -1; 229 | } 230 | 231 | return 0; 232 | } 233 | 234 | errno_t util_parse_options(int argc, char *argv[]) 235 | { 236 | const char *progname = argv[0]; 237 | int flag; 238 | uint64_t num; 239 | 240 | /* Set default options */ 241 | memset(&opts, 0, sizeof(opts)); 242 | 243 | /* Invalid titleid for verification if it's been set */ 244 | opts.titleid = 0xFFFFFFFFFFFFFFFFULL; 245 | 246 | while ((flag = getopt(argc, argv, "cDhk:K:lmprvV:")) != -1) { 247 | switch (flag) { 248 | case 'D': 249 | opts.flags |= OPT_DEV_KEYS; 250 | break; 251 | case 'c': 252 | if (opts.flags & OPT_HAS_KEY) { 253 | err("You cannot specify -k/-K and -c/-D together."); 254 | return -1; 255 | } 256 | 257 | opts.flags |= (OPT_DECRYPT_FROM_CETK | OPT_KEY_ENCRYPTED); 258 | break; 259 | 260 | case 'K': 261 | opts.flags |= OPT_KEY_ENCRYPTED; 262 | /* fallthrough */ 263 | case 'k': 264 | if (opts.flags & OPT_HAS_KEY) { 265 | err("You may only specify one key."); 266 | return -1; 267 | } 268 | 269 | if (opts.flags & OPT_DECRYPT_FROM_CETK) { 270 | err("You cannot specify -k/-K and -c together."); 271 | return -1; 272 | } 273 | 274 | if (util_parse_hex(optarg, opts.key, sizeof(opts.key)) 275 | != 0) { 276 | err("Error: Unable to parse key %s.", optarg); 277 | return -1; 278 | } 279 | 280 | opts.flags |= OPT_HAS_KEY; 281 | 282 | break; 283 | 284 | case 'l': 285 | opts.flags |= OPT_LOCAL_FILES; 286 | break; 287 | 288 | case 'm': 289 | opts.flags |= OPT_KEEP_META; 290 | break; 291 | 292 | case 'p': 293 | opts.flags |= OPT_SHOW_PROGRESS; 294 | break; 295 | 296 | case 'r': 297 | opts.flags |= OPT_RESUME; 298 | break; 299 | 300 | case 'V': 301 | if (util_parse_num(optarg, (char)flag, &num, 0, 0, 302 | 0xFFFFU) != 0) 303 | return -1; 304 | 305 | opts.flags |= OPT_HAS_VERSION; 306 | opts.version = (uint16_t)num; 307 | 308 | break; 309 | 310 | case 'v': 311 | version(); 312 | return 1; 313 | 314 | case 'h': 315 | help(argv[0]); 316 | return 1; 317 | 318 | case '?': 319 | err("Error: Unknown flag or missing flag argument."); 320 | help(argv[0]); 321 | return -1; 322 | } 323 | } 324 | 325 | argc -= optind; 326 | argv += optind; 327 | 328 | if (argv[0] == NULL) { 329 | err("Error: No title ID given."); 330 | help(progname); 331 | return -1; 332 | } 333 | 334 | if (util_parse_num(argv[0], 0, &num, 16, 335 | /* Minimum TID on the Wii */ 336 | 0x0000000100000001ULL, 337 | /* Maximum theoretical TID */ 338 | 0xFFFFFFFFFFFFFFFFULL) != 0) 339 | return -1; 340 | 341 | opts.titleid = num; 342 | 343 | return 0; 344 | } 345 | 346 | errno_t util_create_file(const char *path) 347 | { 348 | int fd; 349 | 350 | if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) == -1) 351 | return -1; 352 | 353 | if (close(fd) != 0) 354 | return -1; 355 | 356 | return 0; 357 | } 358 | 359 | errno_t util_get_file_size(const char *path, uint64_t *size) 360 | { 361 | struct stat buf; 362 | 363 | errno = 0; 364 | if (stat(path, &buf) != 0) { 365 | err("Unable to get file size: %s", strerror(errno)); 366 | return -1; 367 | } 368 | 369 | *size = (uint64_t)buf.st_size; 370 | 371 | return 0; 372 | } 373 | 374 | uint8_t util_get_msb64(uint64_t i) 375 | { 376 | uint8_t msb = 0; 377 | 378 | while (i >>= 1) 379 | ++msb; 380 | 381 | return msb; 382 | } 383 | 384 | __attribute__((format(printf, 3, 4))) char *util_realloc_and_append_fmt( 385 | char *base, size_t appendlen, const char *fmt, ...) 386 | { 387 | size_t newlen, baselen; 388 | char *newbuf; 389 | va_list ap; 390 | 391 | baselen = strlen(base); 392 | newlen = baselen + appendlen; 393 | 394 | if ((newbuf = realloc(base, newlen + 1)) == NULL) { 395 | free(base); 396 | return NULL; 397 | } 398 | 399 | va_start(ap, fmt); 400 | vsprintf(newbuf + baselen, fmt, ap); 401 | va_end(ap); 402 | 403 | return newbuf; 404 | } 405 | 406 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | #ifndef NUSTOOL_UTIL_H 2 | #define NUSTOOL_UTIL_H 3 | 4 | #include "types.h" 5 | 6 | #define MAX_FILEPATH_LEN (16+1+6+1+9) 7 | #define MAX_FILENAME_LEN (9) 8 | 9 | void oom(void) __attribute__((noreturn)); 10 | void err(const char *fmt, ...) __attribute__((format(printf, 1, 2))); 11 | void msg(const char *fmt, ...) __attribute__((format(printf, 1, 2))); 12 | errno_t util_parse_hex(char *in, byte *out, size_t outlen); 13 | errno_t util_create_outdir(void); 14 | char *util_get_filepath(const char *path); 15 | const char *util_print_hex(const byte bytes[], size_t length, char *out); 16 | errno_t util_parse_options(int argc, char *argv[]); 17 | errno_t util_create_file(const char *path); 18 | errno_t util_get_file_size(const char *path, uint64_t *size); 19 | uint8_t util_get_msb64(uint64_t i); 20 | char *util_realloc_and_append_fmt(char *base, size_t appendlen, const char *fmt, 21 | ...) __attribute__((format(printf, 3, 4))); 22 | 23 | #endif 24 | 25 | -------------------------------------------------------------------------------- /version.h: -------------------------------------------------------------------------------- 1 | #ifndef NUSTOOL_VERSION_H 2 | #define NUSTOOL_VERSION_H 3 | 4 | #define NUSTOOL_VERSION "1.1" 5 | 6 | #endif 7 | 8 | --------------------------------------------------------------------------------