Port title database and cdecrypt to Go (#87)

* Experimental removal of cdecrypt

* Pushing before cdecrypt port

* Some progress...

* Replace title database with native Go

* Update title db url

* Almost working decryption and extraction

* Almost there

* Remove unnecessary type conversion

* Fix directory structure creation

* Finally fix decryption

* Cleanup print statements

* Do not write FST to file

* Add progress

* Add encrypted contents decryption
This commit is contained in:
Xpl0itU 2024-03-31 19:38:13 +02:00 committed by GitHub
parent b031be4ecd
commit e40d499b72
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 589 additions and 2897 deletions

1
.gitignore vendored
View file

@ -9,3 +9,4 @@ main
*.a
out/
log.txt
db.go

View file

@ -1,869 +0,0 @@
/*
* FIPS-197 compliant AES implementation
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is part of mbed TLS (https://tls.mbed.org) and was downloaded from
* https://github.com/Secure-Embedded-Systems/RSA-example/tree/master/library/.
*/
/*
* The AES block cipher was designed by Vincent Rijmen and Joan Daemen.
*
* http://csrc.nist.gov/encryption/aes/rijndael/Rijndael.pdf
* http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
*/
#include "aes.h"
#include <string.h>
/* Implementation that should never be optimized out by the compiler */
static void zeroize(void *v, size_t n) {
volatile uint8_t *p = (uint8_t *)v;
while (n--)
*p++ = 0;
}
/*
* 32-bit integer manipulation macros (little endian)
*/
#ifndef GET_UINT32_LE
#define GET_UINT32_LE(n, b, i) \
{ \
(n) = ((uint32_t)(b)[(i)]) | ((uint32_t)(b)[(i) + 1] << 8) | \
((uint32_t)(b)[(i) + 2] << 16) | ((uint32_t)(b)[(i) + 3] << 24); \
}
#endif
#ifndef PUT_UINT32_LE
#define PUT_UINT32_LE(n, b, i) \
{ \
(b)[(i)] = (uint8_t)(((n)) & 0xFF); \
(b)[(i) + 1] = (uint8_t)(((n) >> 8) & 0xFF); \
(b)[(i) + 2] = (uint8_t)(((n) >> 16) & 0xFF); \
(b)[(i) + 3] = (uint8_t)(((n) >> 24) & 0xFF); \
}
#endif
#if defined(AES_ROM_TABLES)
/*
* Forward S-box
*/
static const uint8_t FSb[256] = {
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B,
0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0,
0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26,
0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2,
0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0,
0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED,
0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F,
0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5,
0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC,
0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14,
0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C,
0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D,
0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F,
0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E,
0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11,
0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F,
0xB0, 0x54, 0xBB, 0x16};
/*
* Forward tables
*/
#define FT \
\
V(A5, 63, 63, C6), V(84, 7C, 7C, F8), V(99, 77, 77, EE), V(8D, 7B, 7B, F6), \
V(0D, F2, F2, FF), V(BD, 6B, 6B, D6), V(B1, 6F, 6F, DE), \
V(54, C5, C5, 91), V(50, 30, 30, 60), V(03, 01, 01, 02), \
V(A9, 67, 67, CE), V(7D, 2B, 2B, 56), V(19, FE, FE, E7), \
V(62, D7, D7, B5), V(E6, AB, AB, 4D), V(9A, 76, 76, EC), \
V(45, CA, CA, 8F), V(9D, 82, 82, 1F), V(40, C9, C9, 89), \
V(87, 7D, 7D, FA), V(15, FA, FA, EF), V(EB, 59, 59, B2), \
V(C9, 47, 47, 8E), V(0B, F0, F0, FB), V(EC, AD, AD, 41), \
V(67, D4, D4, B3), V(FD, A2, A2, 5F), V(EA, AF, AF, 45), \
V(BF, 9C, 9C, 23), V(F7, A4, A4, 53), V(96, 72, 72, E4), \
V(5B, C0, C0, 9B), V(C2, B7, B7, 75), V(1C, FD, FD, E1), \
V(AE, 93, 93, 3D), V(6A, 26, 26, 4C), V(5A, 36, 36, 6C), \
V(41, 3F, 3F, 7E), V(02, F7, F7, F5), V(4F, CC, CC, 83), \
V(5C, 34, 34, 68), V(F4, A5, A5, 51), V(34, E5, E5, D1), \
V(08, F1, F1, F9), V(93, 71, 71, E2), V(73, D8, D8, AB), \
V(53, 31, 31, 62), V(3F, 15, 15, 2A), V(0C, 04, 04, 08), \
V(52, C7, C7, 95), V(65, 23, 23, 46), V(5E, C3, C3, 9D), \
V(28, 18, 18, 30), V(A1, 96, 96, 37), V(0F, 05, 05, 0A), \
V(B5, 9A, 9A, 2F), V(09, 07, 07, 0E), V(36, 12, 12, 24), \
V(9B, 80, 80, 1B), V(3D, E2, E2, DF), V(26, EB, EB, CD), \
V(69, 27, 27, 4E), V(CD, B2, B2, 7F), V(9F, 75, 75, EA), \
V(1B, 09, 09, 12), V(9E, 83, 83, 1D), V(74, 2C, 2C, 58), \
V(2E, 1A, 1A, 34), V(2D, 1B, 1B, 36), V(B2, 6E, 6E, DC), \
V(EE, 5A, 5A, B4), V(FB, A0, A0, 5B), V(F6, 52, 52, A4), \
V(4D, 3B, 3B, 76), V(61, D6, D6, B7), V(CE, B3, B3, 7D), \
V(7B, 29, 29, 52), V(3E, E3, E3, DD), V(71, 2F, 2F, 5E), \
V(97, 84, 84, 13), V(F5, 53, 53, A6), V(68, D1, D1, B9), \
V(00, 00, 00, 00), V(2C, ED, ED, C1), V(60, 20, 20, 40), \
V(1F, FC, FC, E3), V(C8, B1, B1, 79), V(ED, 5B, 5B, B6), \
V(BE, 6A, 6A, D4), V(46, CB, CB, 8D), V(D9, BE, BE, 67), \
V(4B, 39, 39, 72), V(DE, 4A, 4A, 94), V(D4, 4C, 4C, 98), \
V(E8, 58, 58, B0), V(4A, CF, CF, 85), V(6B, D0, D0, BB), \
V(2A, EF, EF, C5), V(E5, AA, AA, 4F), V(16, FB, FB, ED), \
V(C5, 43, 43, 86), V(D7, 4D, 4D, 9A), V(55, 33, 33, 66), \
V(94, 85, 85, 11), V(CF, 45, 45, 8A), V(10, F9, F9, E9), \
V(06, 02, 02, 04), V(81, 7F, 7F, FE), V(F0, 50, 50, A0), \
V(44, 3C, 3C, 78), V(BA, 9F, 9F, 25), V(E3, A8, A8, 4B), \
V(F3, 51, 51, A2), V(FE, A3, A3, 5D), V(C0, 40, 40, 80), \
V(8A, 8F, 8F, 05), V(AD, 92, 92, 3F), V(BC, 9D, 9D, 21), \
V(48, 38, 38, 70), V(04, F5, F5, F1), V(DF, BC, BC, 63), \
V(C1, B6, B6, 77), V(75, DA, DA, AF), V(63, 21, 21, 42), \
V(30, 10, 10, 20), V(1A, FF, FF, E5), V(0E, F3, F3, FD), \
V(6D, D2, D2, BF), V(4C, CD, CD, 81), V(14, 0C, 0C, 18), \
V(35, 13, 13, 26), V(2F, EC, EC, C3), V(E1, 5F, 5F, BE), \
V(A2, 97, 97, 35), V(CC, 44, 44, 88), V(39, 17, 17, 2E), \
V(57, C4, C4, 93), V(F2, A7, A7, 55), V(82, 7E, 7E, FC), \
V(47, 3D, 3D, 7A), V(AC, 64, 64, C8), V(E7, 5D, 5D, BA), \
V(2B, 19, 19, 32), V(95, 73, 73, E6), V(A0, 60, 60, C0), \
V(98, 81, 81, 19), V(D1, 4F, 4F, 9E), V(7F, DC, DC, A3), \
V(66, 22, 22, 44), V(7E, 2A, 2A, 54), V(AB, 90, 90, 3B), \
V(83, 88, 88, 0B), V(CA, 46, 46, 8C), V(29, EE, EE, C7), \
V(D3, B8, B8, 6B), V(3C, 14, 14, 28), V(79, DE, DE, A7), \
V(E2, 5E, 5E, BC), V(1D, 0B, 0B, 16), V(76, DB, DB, AD), \
V(3B, E0, E0, DB), V(56, 32, 32, 64), V(4E, 3A, 3A, 74), \
V(1E, 0A, 0A, 14), V(DB, 49, 49, 92), V(0A, 06, 06, 0C), \
V(6C, 24, 24, 48), V(E4, 5C, 5C, B8), V(5D, C2, C2, 9F), \
V(6E, D3, D3, BD), V(EF, AC, AC, 43), V(A6, 62, 62, C4), \
V(A8, 91, 91, 39), V(A4, 95, 95, 31), V(37, E4, E4, D3), \
V(8B, 79, 79, F2), V(32, E7, E7, D5), V(43, C8, C8, 8B), \
V(59, 37, 37, 6E), V(B7, 6D, 6D, DA), V(8C, 8D, 8D, 01), \
V(64, D5, D5, B1), V(D2, 4E, 4E, 9C), V(E0, A9, A9, 49), \
V(B4, 6C, 6C, D8), V(FA, 56, 56, AC), V(07, F4, F4, F3), \
V(25, EA, EA, CF), V(AF, 65, 65, CA), V(8E, 7A, 7A, F4), \
V(E9, AE, AE, 47), V(18, 08, 08, 10), V(D5, BA, BA, 6F), \
V(88, 78, 78, F0), V(6F, 25, 25, 4A), V(72, 2E, 2E, 5C), \
V(24, 1C, 1C, 38), V(F1, A6, A6, 57), V(C7, B4, B4, 73), \
V(51, C6, C6, 97), V(23, E8, E8, CB), V(7C, DD, DD, A1), \
V(9C, 74, 74, E8), V(21, 1F, 1F, 3E), V(DD, 4B, 4B, 96), \
V(DC, BD, BD, 61), V(86, 8B, 8B, 0D), V(85, 8A, 8A, 0F), \
V(90, 70, 70, E0), V(42, 3E, 3E, 7C), V(C4, B5, B5, 71), \
V(AA, 66, 66, CC), V(D8, 48, 48, 90), V(05, 03, 03, 06), \
V(01, F6, F6, F7), V(12, 0E, 0E, 1C), V(A3, 61, 61, C2), \
V(5F, 35, 35, 6A), V(F9, 57, 57, AE), V(D0, B9, B9, 69), \
V(91, 86, 86, 17), V(58, C1, C1, 99), V(27, 1D, 1D, 3A), \
V(B9, 9E, 9E, 27), V(38, E1, E1, D9), V(13, F8, F8, EB), \
V(B3, 98, 98, 2B), V(33, 11, 11, 22), V(BB, 69, 69, D2), \
V(70, D9, D9, A9), V(89, 8E, 8E, 07), V(A7, 94, 94, 33), \
V(B6, 9B, 9B, 2D), V(22, 1E, 1E, 3C), V(92, 87, 87, 15), \
V(20, E9, E9, C9), V(49, CE, CE, 87), V(FF, 55, 55, AA), \
V(78, 28, 28, 50), V(7A, DF, DF, A5), V(8F, 8C, 8C, 03), \
V(F8, A1, A1, 59), V(80, 89, 89, 09), V(17, 0D, 0D, 1A), \
V(DA, BF, BF, 65), V(31, E6, E6, D7), V(C6, 42, 42, 84), \
V(B8, 68, 68, D0), V(C3, 41, 41, 82), V(B0, 99, 99, 29), \
V(77, 2D, 2D, 5A), V(11, 0F, 0F, 1E), V(CB, B0, B0, 7B), \
V(FC, 54, 54, A8), V(D6, BB, BB, 6D), V(3A, 16, 16, 2C)
#define V(a, b, c, d) 0x##a##b##c##d
static const uint32_t FT0[256] = {FT};
#undef V
#define V(a, b, c, d) 0x##b##c##d##a
static const uint32_t FT1[256] = {FT};
#undef V
#define V(a, b, c, d) 0x##c##d##a##b
static const uint32_t FT2[256] = {FT};
#undef V
#define V(a, b, c, d) 0x##d##a##b##c
static const uint32_t FT3[256] = {FT};
#undef V
#undef FT
/*
* Reverse S-box
*/
static const uint8_t RSb[256] = {
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E,
0x81, 0xF3, 0xD7, 0xFB, 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87,
0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, 0x54, 0x7B, 0x94, 0x32,
0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49,
0x6D, 0x8B, 0xD1, 0x25, 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16,
0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 0x6C, 0x70, 0x48, 0x50,
0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05,
0xB8, 0xB3, 0x45, 0x06, 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02,
0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 0x3A, 0x91, 0x11, 0x41,
0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8,
0x1C, 0x75, 0xDF, 0x6E, 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89,
0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x3E, 0x4B,
0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59,
0x27, 0x80, 0xEC, 0x5F, 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D,
0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 0xA0, 0xE0, 0x3B, 0x4D,
0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63,
0x55, 0x21, 0x0C, 0x7D};
/*
* Reverse tables
*/
#define RT \
\
V(50, A7, F4, 51), V(53, 65, 41, 7E), V(C3, A4, 17, 1A), V(96, 5E, 27, 3A), \
V(CB, 6B, AB, 3B), V(F1, 45, 9D, 1F), V(AB, 58, FA, AC), \
V(93, 03, E3, 4B), V(55, FA, 30, 20), V(F6, 6D, 76, AD), \
V(91, 76, CC, 88), V(25, 4C, 02, F5), V(FC, D7, E5, 4F), \
V(D7, CB, 2A, C5), V(80, 44, 35, 26), V(8F, A3, 62, B5), \
V(49, 5A, B1, DE), V(67, 1B, BA, 25), V(98, 0E, EA, 45), \
V(E1, C0, FE, 5D), V(02, 75, 2F, C3), V(12, F0, 4C, 81), \
V(A3, 97, 46, 8D), V(C6, F9, D3, 6B), V(E7, 5F, 8F, 03), \
V(95, 9C, 92, 15), V(EB, 7A, 6D, BF), V(DA, 59, 52, 95), \
V(2D, 83, BE, D4), V(D3, 21, 74, 58), V(29, 69, E0, 49), \
V(44, C8, C9, 8E), V(6A, 89, C2, 75), V(78, 79, 8E, F4), \
V(6B, 3E, 58, 99), V(DD, 71, B9, 27), V(B6, 4F, E1, BE), \
V(17, AD, 88, F0), V(66, AC, 20, C9), V(B4, 3A, CE, 7D), \
V(18, 4A, DF, 63), V(82, 31, 1A, E5), V(60, 33, 51, 97), \
V(45, 7F, 53, 62), V(E0, 77, 64, B1), V(84, AE, 6B, BB), \
V(1C, A0, 81, FE), V(94, 2B, 08, F9), V(58, 68, 48, 70), \
V(19, FD, 45, 8F), V(87, 6C, DE, 94), V(B7, F8, 7B, 52), \
V(23, D3, 73, AB), V(E2, 02, 4B, 72), V(57, 8F, 1F, E3), \
V(2A, AB, 55, 66), V(07, 28, EB, B2), V(03, C2, B5, 2F), \
V(9A, 7B, C5, 86), V(A5, 08, 37, D3), V(F2, 87, 28, 30), \
V(B2, A5, BF, 23), V(BA, 6A, 03, 02), V(5C, 82, 16, ED), \
V(2B, 1C, CF, 8A), V(92, B4, 79, A7), V(F0, F2, 07, F3), \
V(A1, E2, 69, 4E), V(CD, F4, DA, 65), V(D5, BE, 05, 06), \
V(1F, 62, 34, D1), V(8A, FE, A6, C4), V(9D, 53, 2E, 34), \
V(A0, 55, F3, A2), V(32, E1, 8A, 05), V(75, EB, F6, A4), \
V(39, EC, 83, 0B), V(AA, EF, 60, 40), V(06, 9F, 71, 5E), \
V(51, 10, 6E, BD), V(F9, 8A, 21, 3E), V(3D, 06, DD, 96), \
V(AE, 05, 3E, DD), V(46, BD, E6, 4D), V(B5, 8D, 54, 91), \
V(05, 5D, C4, 71), V(6F, D4, 06, 04), V(FF, 15, 50, 60), \
V(24, FB, 98, 19), V(97, E9, BD, D6), V(CC, 43, 40, 89), \
V(77, 9E, D9, 67), V(BD, 42, E8, B0), V(88, 8B, 89, 07), \
V(38, 5B, 19, E7), V(DB, EE, C8, 79), V(47, 0A, 7C, A1), \
V(E9, 0F, 42, 7C), V(C9, 1E, 84, F8), V(00, 00, 00, 00), \
V(83, 86, 80, 09), V(48, ED, 2B, 32), V(AC, 70, 11, 1E), \
V(4E, 72, 5A, 6C), V(FB, FF, 0E, FD), V(56, 38, 85, 0F), \
V(1E, D5, AE, 3D), V(27, 39, 2D, 36), V(64, D9, 0F, 0A), \
V(21, A6, 5C, 68), V(D1, 54, 5B, 9B), V(3A, 2E, 36, 24), \
V(B1, 67, 0A, 0C), V(0F, E7, 57, 93), V(D2, 96, EE, B4), \
V(9E, 91, 9B, 1B), V(4F, C5, C0, 80), V(A2, 20, DC, 61), \
V(69, 4B, 77, 5A), V(16, 1A, 12, 1C), V(0A, BA, 93, E2), \
V(E5, 2A, A0, C0), V(43, E0, 22, 3C), V(1D, 17, 1B, 12), \
V(0B, 0D, 09, 0E), V(AD, C7, 8B, F2), V(B9, A8, B6, 2D), \
V(C8, A9, 1E, 14), V(85, 19, F1, 57), V(4C, 07, 75, AF), \
V(BB, DD, 99, EE), V(FD, 60, 7F, A3), V(9F, 26, 01, F7), \
V(BC, F5, 72, 5C), V(C5, 3B, 66, 44), V(34, 7E, FB, 5B), \
V(76, 29, 43, 8B), V(DC, C6, 23, CB), V(68, FC, ED, B6), \
V(63, F1, E4, B8), V(CA, DC, 31, D7), V(10, 85, 63, 42), \
V(40, 22, 97, 13), V(20, 11, C6, 84), V(7D, 24, 4A, 85), \
V(F8, 3D, BB, D2), V(11, 32, F9, AE), V(6D, A1, 29, C7), \
V(4B, 2F, 9E, 1D), V(F3, 30, B2, DC), V(EC, 52, 86, 0D), \
V(D0, E3, C1, 77), V(6C, 16, B3, 2B), V(99, B9, 70, A9), \
V(FA, 48, 94, 11), V(22, 64, E9, 47), V(C4, 8C, FC, A8), \
V(1A, 3F, F0, A0), V(D8, 2C, 7D, 56), V(EF, 90, 33, 22), \
V(C7, 4E, 49, 87), V(C1, D1, 38, D9), V(FE, A2, CA, 8C), \
V(36, 0B, D4, 98), V(CF, 81, F5, A6), V(28, DE, 7A, A5), \
V(26, 8E, B7, DA), V(A4, BF, AD, 3F), V(E4, 9D, 3A, 2C), \
V(0D, 92, 78, 50), V(9B, CC, 5F, 6A), V(62, 46, 7E, 54), \
V(C2, 13, 8D, F6), V(E8, B8, D8, 90), V(5E, F7, 39, 2E), \
V(F5, AF, C3, 82), V(BE, 80, 5D, 9F), V(7C, 93, D0, 69), \
V(A9, 2D, D5, 6F), V(B3, 12, 25, CF), V(3B, 99, AC, C8), \
V(A7, 7D, 18, 10), V(6E, 63, 9C, E8), V(7B, BB, 3B, DB), \
V(09, 78, 26, CD), V(F4, 18, 59, 6E), V(01, B7, 9A, EC), \
V(A8, 9A, 4F, 83), V(65, 6E, 95, E6), V(7E, E6, FF, AA), \
V(08, CF, BC, 21), V(E6, E8, 15, EF), V(D9, 9B, E7, BA), \
V(CE, 36, 6F, 4A), V(D4, 09, 9F, EA), V(D6, 7C, B0, 29), \
V(AF, B2, A4, 31), V(31, 23, 3F, 2A), V(30, 94, A5, C6), \
V(C0, 66, A2, 35), V(37, BC, 4E, 74), V(A6, CA, 82, FC), \
V(B0, D0, 90, E0), V(15, D8, A7, 33), V(4A, 98, 04, F1), \
V(F7, DA, EC, 41), V(0E, 50, CD, 7F), V(2F, F6, 91, 17), \
V(8D, D6, 4D, 76), V(4D, B0, EF, 43), V(54, 4D, AA, CC), \
V(DF, 04, 96, E4), V(E3, B5, D1, 9E), V(1B, 88, 6A, 4C), \
V(B8, 1F, 2C, C1), V(7F, 51, 65, 46), V(04, EA, 5E, 9D), \
V(5D, 35, 8C, 01), V(73, 74, 87, FA), V(2E, 41, 0B, FB), \
V(5A, 1D, 67, B3), V(52, D2, DB, 92), V(33, 56, 10, E9), \
V(13, 47, D6, 6D), V(8C, 61, D7, 9A), V(7A, 0C, A1, 37), \
V(8E, 14, F8, 59), V(89, 3C, 13, EB), V(EE, 27, A9, CE), \
V(35, C9, 61, B7), V(ED, E5, 1C, E1), V(3C, B1, 47, 7A), \
V(59, DF, D2, 9C), V(3F, 73, F2, 55), V(79, CE, 14, 18), \
V(BF, 37, C7, 73), V(EA, CD, F7, 53), V(5B, AA, FD, 5F), \
V(14, 6F, 3D, DF), V(86, DB, 44, 78), V(81, F3, AF, CA), \
V(3E, C4, 68, B9), V(2C, 34, 24, 38), V(5F, 40, A3, C2), \
V(72, C3, 1D, 16), V(0C, 25, E2, BC), V(8B, 49, 3C, 28), \
V(41, 95, 0D, FF), V(71, 01, A8, 39), V(DE, B3, 0C, 08), \
V(9C, E4, B4, D8), V(90, C1, 56, 64), V(61, 84, CB, 7B), \
V(70, B6, 32, D5), V(74, 5C, 6C, 48), V(42, 57, B8, D0)
#define V(a, b, c, d) 0x##a##b##c##d
static const uint32_t RT0[256] = {RT};
#undef V
#define V(a, b, c, d) 0x##b##c##d##a
static const uint32_t RT1[256] = {RT};
#undef V
#define V(a, b, c, d) 0x##c##d##a##b
static const uint32_t RT2[256] = {RT};
#undef V
#define V(a, b, c, d) 0x##d##a##b##c
static const uint32_t RT3[256] = {RT};
#undef V
#undef RT
/*
* Round constants
*/
static const uint32_t RCON[10] = {
0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010,
0x00000020, 0x00000040, 0x00000080, 0x0000001B, 0x00000036};
#else /* AES_ROM_TABLES */
/*
* Forward S-box & tables
*/
static uint8_t FSb[256];
static uint32_t FT0[256];
static uint32_t FT1[256];
static uint32_t FT2[256];
static uint32_t FT3[256];
/*
* Reverse S-box & tables
*/
static uint8_t RSb[256];
static uint32_t RT0[256];
static uint32_t RT1[256];
static uint32_t RT2[256];
static uint32_t RT3[256];
/*
* Round constants
*/
static uint32_t RCON[10];
/*
* Tables generation code
*/
#define ROTL8(x) ((x << 8) & 0xFFFFFFFF) | (x >> 24)
#define XTIME(x) ((x << 1) ^ ((x & 0x80) ? 0x1B : 0x00))
#define MUL(x, y) ((x && y) ? pow[(log[x] + log[y]) % 255] : 0)
static int aes_init_done = 0;
static void aes_gen_tables(void) {
int i, x, y, z;
int pow[256];
int log[256];
/*
* compute pow and log tables over GF(2^8)
*/
for (i = 0, x = 1; i < 256; i++) {
pow[i] = x;
log[x] = i;
x = (x ^ XTIME(x)) & 0xFF;
}
/*
* calculate the round constants
*/
for (i = 0, x = 1; i < 10; i++) {
RCON[i] = (uint32_t)x;
x = XTIME(x) & 0xFF;
}
/*
* generate the forward and reverse S-boxes
*/
FSb[0x00] = 0x63;
RSb[0x63] = 0x00;
for (i = 1; i < 256; i++) {
x = pow[255 - log[i]];
y = x;
y = ((y << 1) | (y >> 7)) & 0xFF;
x ^= y;
y = ((y << 1) | (y >> 7)) & 0xFF;
x ^= y;
y = ((y << 1) | (y >> 7)) & 0xFF;
x ^= y;
y = ((y << 1) | (y >> 7)) & 0xFF;
x ^= y ^ 0x63;
FSb[i] = (uint8_t)x;
RSb[x] = (uint8_t)i;
}
/*
* generate the forward and reverse tables
*/
for (i = 0; i < 256; i++) {
x = FSb[i];
y = XTIME(x) & 0xFF;
z = (y ^ x) & 0xFF;
FT0[i] = ((uint32_t)y) ^ ((uint32_t)x << 8) ^ ((uint32_t)x << 16) ^
((uint32_t)z << 24);
FT1[i] = ROTL8(FT0[i]);
FT2[i] = ROTL8(FT1[i]);
FT3[i] = ROTL8(FT2[i]);
x = RSb[i];
RT0[i] = ((uint32_t)MUL(0x0E, x)) ^ ((uint32_t)MUL(0x09, x) << 8) ^
((uint32_t)MUL(0x0D, x) << 16) ^ ((uint32_t)MUL(0x0B, x) << 24);
RT1[i] = ROTL8(RT0[i]);
RT2[i] = ROTL8(RT1[i]);
RT3[i] = ROTL8(RT2[i]);
}
}
#endif /* AES_ROM_TABLES */
void aes_init(aes_context *ctx) { memset(ctx, 0, sizeof(aes_context)); }
void aes_free(aes_context *ctx) {
if (ctx == NULL)
return;
zeroize(ctx, sizeof(aes_context));
}
/*
* AES key schedule (encryption)
*/
int aes_setkey_enc(aes_context *ctx, const uint8_t *key, unsigned int keybits) {
unsigned int i;
uint32_t *RK;
#if !defined(AES_ROM_TABLES)
if (aes_init_done == 0) {
aes_gen_tables();
aes_init_done = 1;
}
#endif
switch (keybits) {
case 128:
ctx->nr = 10;
break;
case 192:
ctx->nr = 12;
break;
case 256:
ctx->nr = 14;
break;
default:
return (ERR_AES_INVALID_KEY_LENGTH);
}
ctx->rk = RK = ctx->buf;
for (i = 0; i < (keybits >> 5); i++) {
GET_UINT32_LE(RK[i], key, i << 2);
}
switch (ctx->nr) {
case 10:
for (i = 0; i < 10; i++, RK += 4) {
RK[4] = RK[0] ^ RCON[i] ^ ((uint32_t)FSb[(RK[3] >> 8) & 0xFF]) ^
((uint32_t)FSb[(RK[3] >> 16) & 0xFF] << 8) ^
((uint32_t)FSb[(RK[3] >> 24) & 0xFF] << 16) ^
((uint32_t)FSb[(RK[3]) & 0xFF] << 24);
RK[5] = RK[1] ^ RK[4];
RK[6] = RK[2] ^ RK[5];
RK[7] = RK[3] ^ RK[6];
}
break;
case 12:
for (i = 0; i < 8; i++, RK += 6) {
RK[6] = RK[0] ^ RCON[i] ^ ((uint32_t)FSb[(RK[5] >> 8) & 0xFF]) ^
((uint32_t)FSb[(RK[5] >> 16) & 0xFF] << 8) ^
((uint32_t)FSb[(RK[5] >> 24) & 0xFF] << 16) ^
((uint32_t)FSb[(RK[5]) & 0xFF] << 24);
RK[7] = RK[1] ^ RK[6];
RK[8] = RK[2] ^ RK[7];
RK[9] = RK[3] ^ RK[8];
RK[10] = RK[4] ^ RK[9];
RK[11] = RK[5] ^ RK[10];
}
break;
case 14:
for (i = 0; i < 7; i++, RK += 8) {
RK[8] = RK[0] ^ RCON[i] ^ ((uint32_t)FSb[(RK[7] >> 8) & 0xFF]) ^
((uint32_t)FSb[(RK[7] >> 16) & 0xFF] << 8) ^
((uint32_t)FSb[(RK[7] >> 24) & 0xFF] << 16) ^
((uint32_t)FSb[(RK[7]) & 0xFF] << 24);
RK[9] = RK[1] ^ RK[8];
RK[10] = RK[2] ^ RK[9];
RK[11] = RK[3] ^ RK[10];
RK[12] = RK[4] ^ ((uint32_t)FSb[(RK[11]) & 0xFF]) ^
((uint32_t)FSb[(RK[11] >> 8) & 0xFF] << 8) ^
((uint32_t)FSb[(RK[11] >> 16) & 0xFF] << 16) ^
((uint32_t)FSb[(RK[11] >> 24) & 0xFF] << 24);
RK[13] = RK[5] ^ RK[12];
RK[14] = RK[6] ^ RK[13];
RK[15] = RK[7] ^ RK[14];
}
break;
}
return (0);
}
/*
* AES key schedule (decryption)
*/
int aes_setkey_dec(aes_context *ctx, const uint8_t *key, unsigned int keybits) {
int i, j, ret;
aes_context cty;
uint32_t *RK;
uint32_t *SK;
aes_init(&cty);
ctx->rk = RK = ctx->buf;
/* Also checks keybits */
if ((ret = aes_setkey_enc(&cty, key, keybits)) != 0)
goto exit;
ctx->nr = cty.nr;
SK = cty.rk + (uintptr_t)cty.nr * 4;
*RK++ = *SK++;
*RK++ = *SK++;
*RK++ = *SK++;
*RK++ = *SK++;
for (i = ctx->nr - 1, SK -= 8; i > 0; i--, SK -= 8) {
for (j = 0; j < 4; j++, SK++) {
*RK++ = RT0[FSb[(*SK) & 0xFF]] ^ RT1[FSb[(*SK >> 8) & 0xFF]] ^
RT2[FSb[(*SK >> 16) & 0xFF]] ^ RT3[FSb[(*SK >> 24) & 0xFF]];
}
}
*RK++ = *SK++;
*RK++ = *SK++;
*RK++ = *SK++;
*RK++ = *SK++;
exit:
aes_free(&cty);
return (ret);
}
#define AES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3) \
{ \
X0 = *RK++ ^ FT0[(Y0)&0xFF] ^ FT1[(Y1 >> 8) & 0xFF] ^ \
FT2[(Y2 >> 16) & 0xFF] ^ FT3[(Y3 >> 24) & 0xFF]; \
\
X1 = *RK++ ^ FT0[(Y1)&0xFF] ^ FT1[(Y2 >> 8) & 0xFF] ^ \
FT2[(Y3 >> 16) & 0xFF] ^ FT3[(Y0 >> 24) & 0xFF]; \
\
X2 = *RK++ ^ FT0[(Y2)&0xFF] ^ FT1[(Y3 >> 8) & 0xFF] ^ \
FT2[(Y0 >> 16) & 0xFF] ^ FT3[(Y1 >> 24) & 0xFF]; \
\
X3 = *RK++ ^ FT0[(Y3)&0xFF] ^ FT1[(Y0 >> 8) & 0xFF] ^ \
FT2[(Y1 >> 16) & 0xFF] ^ FT3[(Y2 >> 24) & 0xFF]; \
}
#define AES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3) \
{ \
X0 = *RK++ ^ RT0[(Y0)&0xFF] ^ RT1[(Y3 >> 8) & 0xFF] ^ \
RT2[(Y2 >> 16) & 0xFF] ^ RT3[(Y1 >> 24) & 0xFF]; \
\
X1 = *RK++ ^ RT0[(Y1)&0xFF] ^ RT1[(Y0 >> 8) & 0xFF] ^ \
RT2[(Y3 >> 16) & 0xFF] ^ RT3[(Y2 >> 24) & 0xFF]; \
\
X2 = *RK++ ^ RT0[(Y2)&0xFF] ^ RT1[(Y1 >> 8) & 0xFF] ^ \
RT2[(Y0 >> 16) & 0xFF] ^ RT3[(Y3 >> 24) & 0xFF]; \
\
X3 = *RK++ ^ RT0[(Y3)&0xFF] ^ RT1[(Y2 >> 8) & 0xFF] ^ \
RT2[(Y1 >> 16) & 0xFF] ^ RT3[(Y0 >> 24) & 0xFF]; \
}
/*
* AES-ECB block encryption
*/
void aes_encrypt(aes_context *ctx, const uint8_t input[16],
uint8_t output[16]) {
int i;
uint32_t *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3;
RK = ctx->rk;
GET_UINT32_LE(X0, input, 0);
X0 ^= *RK++;
GET_UINT32_LE(X1, input, 4);
X1 ^= *RK++;
GET_UINT32_LE(X2, input, 8);
X2 ^= *RK++;
GET_UINT32_LE(X3, input, 12);
X3 ^= *RK++;
for (i = (ctx->nr >> 1) - 1; i > 0; i--) {
AES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
AES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3);
}
AES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
X0 = *RK++ ^ ((uint32_t)FSb[(Y0)&0xFF]) ^
((uint32_t)FSb[(Y1 >> 8) & 0xFF] << 8) ^
((uint32_t)FSb[(Y2 >> 16) & 0xFF] << 16) ^
((uint32_t)FSb[(Y3 >> 24) & 0xFF] << 24);
X1 = *RK++ ^ ((uint32_t)FSb[(Y1)&0xFF]) ^
((uint32_t)FSb[(Y2 >> 8) & 0xFF] << 8) ^
((uint32_t)FSb[(Y3 >> 16) & 0xFF] << 16) ^
((uint32_t)FSb[(Y0 >> 24) & 0xFF] << 24);
X2 = *RK++ ^ ((uint32_t)FSb[(Y2)&0xFF]) ^
((uint32_t)FSb[(Y3 >> 8) & 0xFF] << 8) ^
((uint32_t)FSb[(Y0 >> 16) & 0xFF] << 16) ^
((uint32_t)FSb[(Y1 >> 24) & 0xFF] << 24);
X3 = *RK++ ^ ((uint32_t)FSb[(Y3)&0xFF]) ^
((uint32_t)FSb[(Y0 >> 8) & 0xFF] << 8) ^
((uint32_t)FSb[(Y1 >> 16) & 0xFF] << 16) ^
((uint32_t)FSb[(Y2 >> 24) & 0xFF] << 24);
PUT_UINT32_LE(X0, output, 0);
PUT_UINT32_LE(X1, output, 4);
PUT_UINT32_LE(X2, output, 8);
PUT_UINT32_LE(X3, output, 12);
}
/*
* AES-ECB block decryption
*/
void aes_decrypt(aes_context *ctx, const uint8_t input[16],
uint8_t output[16]) {
int i;
uint32_t *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3;
RK = ctx->rk;
GET_UINT32_LE(X0, input, 0);
X0 ^= *RK++;
GET_UINT32_LE(X1, input, 4);
X1 ^= *RK++;
GET_UINT32_LE(X2, input, 8);
X2 ^= *RK++;
GET_UINT32_LE(X3, input, 12);
X3 ^= *RK++;
for (i = (ctx->nr >> 1) - 1; i > 0; i--) {
AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
AES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3);
}
AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
X0 = *RK++ ^ ((uint32_t)RSb[(Y0)&0xFF]) ^
((uint32_t)RSb[(Y3 >> 8) & 0xFF] << 8) ^
((uint32_t)RSb[(Y2 >> 16) & 0xFF] << 16) ^
((uint32_t)RSb[(Y1 >> 24) & 0xFF] << 24);
X1 = *RK++ ^ ((uint32_t)RSb[(Y1)&0xFF]) ^
((uint32_t)RSb[(Y0 >> 8) & 0xFF] << 8) ^
((uint32_t)RSb[(Y3 >> 16) & 0xFF] << 16) ^
((uint32_t)RSb[(Y2 >> 24) & 0xFF] << 24);
X2 = *RK++ ^ ((uint32_t)RSb[(Y2)&0xFF]) ^
((uint32_t)RSb[(Y1 >> 8) & 0xFF] << 8) ^
((uint32_t)RSb[(Y0 >> 16) & 0xFF] << 16) ^
((uint32_t)RSb[(Y3 >> 24) & 0xFF] << 24);
X3 = *RK++ ^ ((uint32_t)RSb[(Y3)&0xFF]) ^
((uint32_t)RSb[(Y2 >> 8) & 0xFF] << 8) ^
((uint32_t)RSb[(Y1 >> 16) & 0xFF] << 16) ^
((uint32_t)RSb[(Y0 >> 24) & 0xFF] << 24);
PUT_UINT32_LE(X0, output, 0);
PUT_UINT32_LE(X1, output, 4);
PUT_UINT32_LE(X2, output, 8);
PUT_UINT32_LE(X3, output, 12);
}
/*
* AES-ECB block encryption/decryption
*/
int aes_crypt_ecb(aes_context *ctx, int mode, const uint8_t input[16],
uint8_t output[16]) {
if (mode == AES_ENCRYPT)
aes_encrypt(ctx, input, output);
else
aes_decrypt(ctx, input, output);
return (0);
}
/*
* AES-CBC buffer encryption/decryption
*/
int aes_crypt_cbc(aes_context *ctx, int mode, size_t length, uint8_t iv[16],
const uint8_t *input, uint8_t *output) {
int i;
uint8_t temp[16];
if (length % 16)
return (ERR_AES_INVALID_INPUT_LENGTH);
if (mode == AES_DECRYPT) {
while (length > 0) {
memcpy(temp, input, 16);
aes_crypt_ecb(ctx, mode, input, output);
for (i = 0; i < 16; i++)
output[i] = (uint8_t)(output[i] ^ iv[i]);
memcpy(iv, temp, 16);
input += 16;
output += 16;
length -= 16;
}
} else {
while (length > 0) {
for (i = 0; i < 16; i++)
output[i] = (uint8_t)(input[i] ^ iv[i]);
aes_crypt_ecb(ctx, mode, output, output);
memcpy(iv, output, 16);
input += 16;
output += 16;
length -= 16;
}
}
return (0);
}
/*
* AES-CFB128 buffer encryption/decryption
*/
int aes_crypt_cfb128(aes_context *ctx, int mode, size_t length, size_t *iv_off,
uint8_t iv[16], const uint8_t *input, uint8_t *output) {
int c;
size_t n = *iv_off;
if (mode == AES_DECRYPT) {
while (length--) {
if (n == 0)
aes_crypt_ecb(ctx, AES_ENCRYPT, iv, iv);
c = *input++;
*output++ = (uint8_t)(c ^ iv[n]);
iv[n] = (uint8_t)c;
n = (n + 1) & 0x0F;
}
} else {
while (length--) {
if (n == 0)
aes_crypt_ecb(ctx, AES_ENCRYPT, iv, iv);
iv[n] = *output++ = (uint8_t)(iv[n] ^ *input++);
n = (n + 1) & 0x0F;
}
}
*iv_off = n;
return (0);
}
/*
* AES-CFB8 buffer encryption/decryption
*/
int aes_crypt_cfb8(aes_context *ctx, int mode, size_t length, uint8_t iv[16],
const uint8_t *input, uint8_t *output) {
uint8_t c;
uint8_t ov[17];
while (length--) {
memcpy(ov, iv, 16);
aes_crypt_ecb(ctx, AES_ENCRYPT, iv, iv);
if (mode == AES_DECRYPT)
ov[16] = *input;
c = *output++ = (uint8_t)(iv[0] ^ *input++);
if (mode == AES_ENCRYPT)
ov[16] = c;
memcpy(iv, ov + 1, 16);
}
return (0);
}
/*
* AES-CTR buffer encryption/decryption
*/
int aes_crypt_ctr(aes_context *ctx, size_t length, size_t *nc_off,
uint8_t nonce_counter[16], uint8_t stream_block[16],
const uint8_t *input, uint8_t *output) {
int c, i;
size_t n = *nc_off;
while (length--) {
if (n == 0) {
aes_crypt_ecb(ctx, AES_ENCRYPT, nonce_counter, stream_block);
for (i = 16; i > 0; i--)
if (++nonce_counter[i - 1] != 0)
break;
}
c = *input++;
*output++ = (uint8_t)(c ^ stream_block[n]);
n = (n + 1) & 0x0F;
}
*nc_off = n;
return (0);
}

View file

@ -1,236 +0,0 @@
/**
* \file aes.h
*
* \brief AES block cipher
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is part of mbed TLS (https://tls.mbed.org) and was downloaded from
* https://github.com/Secure-Embedded-Systems/RSA-example/tree/master/include/.
*/
#ifndef AES_H
#define AES_H
#include <stddef.h>
#include <stdint.h>
#define AES_ENCRYPT 1
#define AES_DECRYPT 0
#define ERR_AES_INVALID_KEY_LENGTH -0x0020 /**< Invalid key length. */
#define ERR_AES_INVALID_INPUT_LENGTH -0x0022 /**< Invalid data input length. \
*/
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief AES context structure
*
* \note buf is able to hold 32 extra bytes, which can be used:
* - for alignment purposes if VIA padlock is used, and/or
* - to simplify key expansion in the 256-bit case by
* generating an extra round key
*/
typedef struct {
int nr; /*!< number of rounds */
uint32_t *rk; /*!< AES round keys */
uint32_t buf[68]; /*!< unaligned data */
} aes_context;
/**
* \brief Initialize AES context
*
* \param ctx AES context to be initialized
*/
void aes_init(aes_context *ctx);
/**
* \brief Clear AES context
*
* \param ctx AES context to be cleared
*/
void aes_free(aes_context *ctx);
/**
* \brief AES key schedule (encryption)
*
* \param ctx AES context to be initialized
* \param key encryption key
* \param keybits must be 128, 192 or 256
*
* \return 0 if successful, or ERR_AES_INVALID_KEY_LENGTH
*/
int aes_setkey_enc(aes_context *ctx, const uint8_t *key, unsigned int keybits);
/**
* \brief AES key schedule (decryption)
*
* \param ctx AES context to be initialized
* \param key decryption key
* \param keybits must be 128, 192 or 256
*
* \return 0 if successful, or ERR_AES_INVALID_KEY_LENGTH
*/
int aes_setkey_dec(aes_context *ctx, const uint8_t *key, unsigned int keybits);
/**
* \brief AES-ECB block encryption/decryption
*
* \param ctx AES context
* \param mode AES_ENCRYPT or AES_DECRYPT
* \param input 16-byte input block
* \param output 16-byte output block
*
* \return 0 if successful
*/
int aes_crypt_ecb(aes_context *ctx, int mode, const uint8_t input[16],
uint8_t output[16]);
/**
* \brief AES-CBC buffer encryption/decryption
* Length should be a multiple of the block
* size (16 bytes)
*
* \note Upon exit, the content of the IV is updated so that you can
* call the function same function again on the following
* block(s) of data and get the same result as if it was
* encrypted in one call. This allows a "streaming" usage.
* If on the other hand you need to retain the contents of the
* IV, you should either save it manually or use the cipher
* module instead.
*
* \param ctx AES context
* \param mode AES_ENCRYPT or AES_DECRYPT
* \param length length of the input data
* \param iv initialization vector (updated after use)
* \param input buffer holding the input data
* \param output buffer holding the output data
*
* \return 0 if successful, or ERR_AES_INVALID_INPUT_LENGTH
*/
int aes_crypt_cbc(aes_context *ctx, int mode, size_t length, uint8_t iv[16],
const uint8_t *input, uint8_t *output);
/**
* \brief AES-CFB128 buffer encryption/decryption.
*
* Note: Due to the nature of CFB you should use the same key schedule for
* both encryption and decryption. So a context initialized with
* aes_setkey_enc() for both AES_ENCRYPT and AES_DECRYPT.
*
* \note Upon exit, the content of the IV is updated so that you can
* call the function same function again on the following
* block(s) of data and get the same result as if it was
* encrypted in one call. This allows a "streaming" usage.
* If on the other hand you need to retain the contents of the
* IV, you should either save it manually or use the cipher
* module instead.
*
* \param ctx AES context
* \param mode AES_ENCRYPT or AES_DECRYPT
* \param length length of the input data
* \param iv_off offset in IV (updated after use)
* \param iv initialization vector (updated after use)
* \param input buffer holding the input data
* \param output buffer holding the output data
*
* \return 0 if successful
*/
int aes_crypt_cfb128(aes_context *ctx, int mode, size_t length, size_t *iv_off,
uint8_t iv[16], const uint8_t *input, uint8_t *output);
/**
* \brief AES-CFB8 buffer encryption/decryption.
*
* Note: Due to the nature of CFB you should use the same key schedule for
* both encryption and decryption. So a context initialized with
* aes_setkey_enc() for both AES_ENCRYPT and AES_DECRYPT.
*
* \note Upon exit, the content of the IV is updated so that you can
* call the function same function again on the following
* block(s) of data and get the same result as if it was
* encrypted in one call. This allows a "streaming" usage.
* If on the other hand you need to retain the contents of the
* IV, you should either save it manually or use the cipher
* module instead.
*
* \param ctx AES context
* \param mode AES_ENCRYPT or AES_DECRYPT
* \param length length of the input data
* \param iv initialization vector (updated after use)
* \param input buffer holding the input data
* \param output buffer holding the output data
*
* \return 0 if successful
*/
int aes_crypt_cfb8(aes_context *ctx, int mode, size_t length, uint8_t iv[16],
const uint8_t *input, uint8_t *output);
/**
* \brief AES-CTR buffer encryption/decryption
*
* Warning: You have to keep the maximum use of your counter in mind!
*
* Note: Due to the nature of CTR you should use the same key schedule for
* both encryption and decryption. So a context initialized with
* aes_setkey_enc() for both AES_ENCRYPT and AES_DECRYPT.
*
* \param ctx AES context
* \param length The length of the data
* \param nc_off The offset in the current stream_block (for resuming
* within current cipher stream). The offset pointer to
* should be 0 at the start of a stream.
* \param nonce_counter The 128-bit nonce and counter.
* \param stream_block The saved stream-block for resuming. Is overwritten
* by the function.
* \param input The input data stream
* \param output The output data stream
*
* \return 0 if successful
*/
int aes_crypt_ctr(aes_context *ctx, size_t length, size_t *nc_off,
uint8_t nonce_counter[16], uint8_t stream_block[16],
const uint8_t *input, uint8_t *output);
/**
* \brief Internal AES block encryption function
* (Only exposed to allow overriding it,
* see AES_ENCRYPT_ALT)
*
* \param ctx AES context
* \param input Plaintext block
* \param output Output (ciphertext) block
*/
void aes_encrypt(aes_context *ctx, const uint8_t input[16], uint8_t output[16]);
/**
* \brief Internal AES block decryption function
* (Only exposed to allow overriding it,
* see AES_DECRYPT_ALT)
*
* \param ctx AES context
* \param input Ciphertext block
* \param output Output (plaintext) block
*/
void aes_decrypt(aes_context *ctx, const uint8_t input[16], uint8_t output[16]);
#ifdef __cplusplus
}
#endif
#endif /* aes.h */

View file

@ -1,555 +0,0 @@
/*
cdecrypt - Decrypt Wii U NUS content files
Copyright © 2013-2015 crediar <https://code.google.com/p/cdecrypt/>
Copyright © 2020-2022 VitaSmith <https://github.com/VitaSmith/cdecrypt>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aes.h"
#include "cdecrypt.h"
#include "sha1.h"
#include "utf8.h"
#include "util.h"
#define MAX_ENTRIES 90000
#define MAX_LEVELS 16
#define FST_MAGIC 0x46535400 // 'FST\0'
// We use part of the root cert name used by TMD/TIK to identify them
#define TMD_MAGIC 0x4350303030303030ULL // 'CP000000'
#define TIK_MAGIC 0x5853303030303030ULL // 'XS000000'
#define T_MAGIC_OFFSET 0x0150
#define HASH_BLOCK_SIZE 0xFC00
#define HASHES_SIZE 0x0400
static const uint8_t WiiUCommonDevKey[16] = {0x2F, 0x5C, 0x1B, 0x29, 0x44, 0xE7,
0xFD, 0x6F, 0xC3, 0x97, 0x96, 0x4B,
0x05, 0x76, 0x91, 0xFA};
static const uint8_t WiiUCommonKey[16] = {0xD7, 0xB0, 0x04, 0x02, 0x65, 0x9B,
0xA2, 0xAB, 0xD2, 0xCB, 0x0D, 0xB2,
0x7F, 0xA2, 0xB6, 0x56};
aes_context ctx;
uint8_t title_id[16];
uint8_t title_key[16];
uint64_t h0_count = 0;
uint64_t h0_fail = 0;
#pragma pack(1)
enum ContentType {
CONTENT_REQUIRED = (1 << 0), // Not sure
CONTENT_SHARED = (1 << 15),
CONTENT_OPTIONAL = (1 << 14),
};
typedef struct {
uint16_t IndexOffset; // 0 0x204
uint16_t CommandCount; // 2 0x206
uint8_t SHA2[32]; // 12 0x208
} ContentInfo;
typedef struct {
uint32_t ID; // 0 0xB04
uint16_t Index; // 4 0xB08
uint16_t Type; // 6 0xB0A
uint64_t Size; // 8 0xB0C
uint8_t SHA2[32]; // 16 0xB14
} Content;
typedef struct {
uint32_t SignatureType; // 0x000
uint8_t Signature[0x100]; // 0x004
uint8_t Padding0[0x3C]; // 0x104
uint8_t Issuer[0x40]; // 0x140
uint8_t Version; // 0x180
uint8_t CACRLVersion; // 0x181
uint8_t SignerCRLVersion; // 0x182
uint8_t Padding1; // 0x183
uint64_t SystemVersion; // 0x184
uint64_t TitleID; // 0x18C
uint32_t TitleType; // 0x194
uint16_t GroupID; // 0x198
uint8_t Reserved[62]; // 0x19A
uint32_t AccessRights; // 0x1D8
uint16_t TitleVersion; // 0x1DC
uint16_t ContentCount; // 0x1DE
uint16_t BootIndex; // 0x1E0
uint8_t Padding3[2]; // 0x1E2
uint8_t SHA2[32]; // 0x1E4
ContentInfo ContentInfos[64];
Content Contents[]; // 0x1E4
} TitleMetaData;
struct FSTInfo {
uint32_t Unknown;
uint32_t Size;
uint32_t UnknownB;
uint32_t UnknownC[6];
};
struct FST {
uint32_t MagicBytes;
uint32_t Unknown;
uint32_t EntryCount;
uint32_t UnknownB[5];
struct FSTInfo FSTInfos[];
};
struct FEntry {
union {
struct {
uint32_t Type : 8;
uint32_t NameOffset : 24;
};
uint32_t TypeName;
};
union {
struct // File Entry
{
uint32_t FileOffset;
uint32_t FileLength;
};
struct // Dir Entry
{
uint32_t ParentOffset;
uint32_t NextOffset;
};
uint32_t entry[2];
};
uint16_t Flags;
uint16_t ContentID;
};
ProgressCallback progressCallback = NULL;
void set_progress_callback(ProgressCallback cb) {
progressCallback = cb;
}
void report_progress(int progress) {
if (progressCallback != NULL) {
progressCallback(progress);
}
}
static bool file_dump(const char *path, void *buf, size_t len) {
assert(buf != NULL);
assert(len != 0);
FILE *dst = fopen_utf8(path, "wb");
if (dst == NULL) {
return false;
}
bool r = (fwrite(buf, 1, len, dst) == len);
fclose(dst);
return r;
}
#define BLOCK_SIZE 0x10000
static bool extract_file_hash(FILE *src, uint64_t part_data_offset,
uint64_t file_offset, uint64_t size,
const char *path, uint16_t content_id) {
bool r = false;
uint8_t *enc = malloc(BLOCK_SIZE);
uint8_t *dec = malloc(BLOCK_SIZE);
assert(enc != NULL);
assert(dec != NULL);
uint8_t iv[16];
uint8_t hash[SHA_DIGEST_LENGTH];
uint8_t h0[SHA_DIGEST_LENGTH];
uint8_t hashes[HASHES_SIZE];
uint64_t write_size = HASH_BLOCK_SIZE;
uint64_t block_number = (file_offset / HASH_BLOCK_SIZE) & 0x0F;
FILE *dst = fopen_utf8(path, "wb");
if (dst == NULL) {
goto out;
}
uint64_t roffset = file_offset / HASH_BLOCK_SIZE * BLOCK_SIZE;
uint64_t soffset =
file_offset - (file_offset / HASH_BLOCK_SIZE * HASH_BLOCK_SIZE);
if (soffset + size > write_size)
write_size = write_size - soffset;
fseek64(src, part_data_offset + roffset, SEEK_SET);
while (size > 0) {
if (write_size > size)
write_size = size;
if (fread(enc, sizeof(char), BLOCK_SIZE, src) != BLOCK_SIZE) {
goto out;
}
memset(iv, 0, sizeof(iv));
iv[1] = (uint8_t)content_id;
aes_crypt_cbc(&ctx, AES_DECRYPT, HASHES_SIZE, iv, enc, (uint8_t *)hashes);
memcpy(h0, hashes + 0x14 * block_number, SHA_DIGEST_LENGTH);
memcpy(iv, hashes + 0x14 * block_number, sizeof(iv));
if (block_number == 0)
iv[1] ^= content_id;
aes_crypt_cbc(&ctx, AES_DECRYPT, HASH_BLOCK_SIZE, iv, enc + HASHES_SIZE,
dec);
sha1(dec, HASH_BLOCK_SIZE, hash);
if (block_number == 0)
hash[1] ^= content_id;
h0_count++;
if (memcmp(hash, h0, SHA_DIGEST_LENGTH) != 0) {
h0_fail++;
goto out;
}
size -= fwrite(dec + soffset, sizeof(char), (size_t)write_size, dst);
block_number++;
if (block_number >= 16)
block_number = 0;
if (soffset) {
write_size = HASH_BLOCK_SIZE;
soffset = 0;
}
}
r = true;
out:
if (dst != NULL)
fclose(dst);
free(enc);
free(dec);
return r;
}
#undef BLOCK_SIZE
#define BLOCK_SIZE 0x8000
static bool extract_file(FILE *src, uint64_t part_data_offset,
uint64_t file_offset, uint64_t size, const char *path,
uint16_t content_id) {
bool r = false;
uint8_t *enc = malloc(BLOCK_SIZE);
uint8_t *dec = malloc(BLOCK_SIZE);
assert(enc != NULL);
assert(dec != NULL);
// Calc real offset
uint64_t roffset = file_offset / BLOCK_SIZE * BLOCK_SIZE;
uint64_t soffset = file_offset - (file_offset / BLOCK_SIZE * BLOCK_SIZE);
FILE *dst = fopen_utf8(path, "wb");
if (dst == NULL) {
goto out;
}
uint8_t iv[16];
memset(iv, 0, sizeof(iv));
iv[1] = (uint8_t)content_id;
uint64_t write_size = BLOCK_SIZE;
if (soffset + size > write_size)
write_size = write_size - soffset;
fseek64(src, part_data_offset + roffset, SEEK_SET);
while (size > 0) {
if (write_size > size)
write_size = size;
if (fread(enc, sizeof(char), BLOCK_SIZE, src) != BLOCK_SIZE) {
goto out;
}
aes_crypt_cbc(&ctx, AES_DECRYPT, BLOCK_SIZE, iv, (const uint8_t *)(enc),
(uint8_t *)dec);
size -= fwrite(dec + soffset, sizeof(char), (size_t)write_size, dst);
if (soffset) {
write_size = BLOCK_SIZE;
soffset = 0;
}
}
r = true;
out:
if (dst != NULL)
fclose(dst);
free(enc);
free(dec);
return r;
}
#undef BLOCK_SIZE
int cdecrypt_main(int argc, char **argv) {
int r = EXIT_FAILURE;
char str[PATH_MAX], *tmd_path = NULL, *tik_path = NULL;
FILE *src = NULL;
TitleMetaData *tmd = NULL;
uint8_t *tik = NULL, *cnt = NULL;
const char *pattern[] = {"%s%c%08x.app", "%s%c%08X.app", "%s%c%08x",
"%s%c%08X"};
if (argc < 2) {
printf(
"%s %s - Wii U NUS content file decrypter\n"
"Copyright (c) 2020-2023 VitaSmith, Copyright (c) 2013-2015 crediar\n"
"Visit https://github.com/VitaSmith/cdecrypt for official source and "
"downloads.\n\n"
"Usage: %s <file or directory>\n\n"
"This program is free software; you can redistribute it and/or modify "
"it under\n"
"the terms of the GNU General Public License as published by the Free "
"Software\n"
"Foundation; either version 3 of the License or any later version.\n",
_appname(argv[0]), APP_VERSION_STR, _appname(argv[0]));
return EXIT_SUCCESS;
}
if (!is_directory(argv[1])) {
uint8_t *buf = NULL;
uint32_t size =
read_file_max(argv[1], &buf, T_MAGIC_OFFSET + sizeof(uint64_t));
if (size == 0)
goto out;
if (size >= T_MAGIC_OFFSET + sizeof(uint64_t)) {
uint64_t magic = getbe64(&buf[T_MAGIC_OFFSET]);
free(buf);
if (magic == TMD_MAGIC) {
tmd_path = strdup(argv[1]);
if (argc < 3) {
tik_path = strdup(argv[1]);
tik_path[strlen(tik_path) - 2] = 'i';
tik_path[strlen(tik_path) - 1] = 'k';
} else {
tik_path = strdup(argv[2]);
}
} else if (magic == TIK_MAGIC) {
tik_path = strdup(argv[1]);
if (argc < 3) {
tmd_path = strdup(argv[1]);
tmd_path[strlen(tik_path) - 2] = 'm';
tmd_path[strlen(tik_path) - 1] = 'd';
} else {
tmd_path = strdup(argv[2]);
}
}
}
// We'll need the current path for locating files, which we set in argv[1]
argv[1][get_trailing_slash(argv[1])] = 0;
if (argv[1][0] == 0) {
argv[1][0] = '.';
argv[1][1] = 0;
}
}
// If the condition below is true, argv[1] is a directory
if ((tmd_path == NULL) || (tik_path == NULL)) {
size_t size = strlen(argv[1]);
free(tmd_path);
free(tik_path);
tmd_path = calloc(size + 16, 1);
tik_path = calloc(size + 16, 1);
sprintf(tmd_path, "%s%ctitle.tmd", argv[1], PATH_SEP);
sprintf(tik_path, "%s%ctitle.tik", argv[1], PATH_SEP);
}
uint32_t tmd_len = read_file(tmd_path, (uint8_t **)&tmd);
if (tmd_len == 0)
goto out;
uint32_t tik_len = read_file(tik_path, &tik);
if (tik_len == 0)
goto out;
if (tmd->Version != 1) {
goto out;
}
if (strcmp((char *)(&tmd->Issuer), "Root-CA00000003-CP0000000b") == 0) {
aes_setkey_dec(&ctx, WiiUCommonKey, sizeof(WiiUCommonKey) * 8);
} else if (strcmp((char *)(&tmd->Issuer), "Root-CA00000004-CP00000010") ==
0) {
aes_setkey_dec(&ctx, WiiUCommonDevKey, sizeof(WiiUCommonDevKey) * 8);
} else {
goto out;
}
memset(title_id, 0, sizeof(title_id));
memcpy(title_id, &tmd->TitleID, 8);
memcpy(title_key, tik + 0x1BF, 16);
aes_crypt_cbc(&ctx, AES_DECRYPT, sizeof(title_key), title_id, title_key,
title_key);
aes_setkey_dec(&ctx, title_key, sizeof(title_key) * 8);
uint8_t iv[16];
memset(iv, 0, sizeof(iv));
for (uint32_t k = 0; k < (array_size(pattern) / 2); k++) {
sprintf(str, pattern[k], argv[1], PATH_SEP, getbe32(&tmd->Contents[0].ID));
if (is_file(str))
break;
}
uint32_t cnt_len = read_file(str, &cnt);
if (cnt_len == 0) {
for (uint32_t k = (array_size(pattern) / 2); k < array_size(pattern); k++) {
sprintf(str, pattern[k], argv[1], PATH_SEP,
getbe32(&tmd->Contents[0].ID));
if (is_file(str))
break;
}
cnt_len = read_file(str, &cnt);
if (cnt_len == 0)
goto out;
}
if (getbe64(&tmd->Contents[0].Size) != (uint64_t)cnt_len) {
goto out;
}
aes_crypt_cbc(&ctx, AES_DECRYPT, cnt_len, iv, cnt, cnt);
if (getbe32(cnt) != FST_MAGIC) {
sprintf(str, "%s%c%08X.dec", argv[1], PATH_SEP,
getbe32(&tmd->Contents[0].ID));
file_dump(str, cnt, cnt_len);
goto out;
}
struct FST *fst = (struct FST *)cnt;
if (getbe32(&fst->EntryCount) > MAX_ENTRIES) {
goto out;
}
struct FEntry *fe =
(struct FEntry *)(cnt + 0x20 +
(uintptr_t)getbe32(&fst->EntryCount) * 0x20);
uint32_t entries =
getbe32(cnt + 0x20 + (uintptr_t)getbe32(&fst->EntryCount) * 0x20 + 8);
uint32_t name_offset =
0x20 + getbe32(&fst->EntryCount) * 0x20 + entries * 0x10;
char *dst_dir = ((argc <= 2) || is_file(argv[2])) ? argv[1] : argv[2];
create_path(dst_dir);
char path[PATH_MAX] = {0};
uint32_t entry[16];
uint32_t l_entry[16];
uint32_t level = 0;
for (uint32_t i = 1; i < entries; i++) {
report_progress((i * 100) / entries);
if (level > 0) {
while ((level >= 1) && (l_entry[level - 1] == i))
level--;
}
if (fe[i].Type & 1) {
entry[level] = i;
l_entry[level++] = getbe32(&fe[i].NextOffset);
if (level >= MAX_LEVELS) {
break;
}
} else {
uint32_t offset;
memset(path, 0, sizeof(path));
strcpy(path, dst_dir);
for (uint32_t j = 0; j < level; j++) {
path[strlen(path)] = PATH_SEP;
offset = getbe32(&fe[entry[j]].TypeName) & 0x00FFFFFF;
memcpy(path + strlen(path), cnt + name_offset + offset,
strlen((char *)cnt + name_offset + offset));
create_path(path);
}
path[strlen(path)] = PATH_SEP;
offset = getbe32(&fe[i].TypeName) & 0x00FFFFFF;
memcpy(path + strlen(path), cnt + name_offset + offset,
strlen((char *)cnt + name_offset + offset));
uint64_t cnt_offset = ((uint64_t)getbe32(&fe[i].FileOffset));
if ((getbe16(&fe[i].Flags) & 4) == 0)
cnt_offset <<= 5;
uint32_t cnt_file_id =
getbe32(&tmd->Contents[getbe16(&fe[i].ContentID)].ID);
if (!(fe[i].Type & 0x80)) {
uint16_t tmd_flags = tmd->Contents[getbe16(&fe[i].ContentID)].Type;
// Handle upper/lowercase for target as well as files without extension
for (uint32_t k = 0; k < array_size(pattern); k++) {
sprintf(str, pattern[k], argv[1], PATH_SEP, cnt_file_id);
if (is_file(str))
break;
}
src = fopen_utf8(str, "rb");
if (src == NULL) {
goto out;
}
if ((getbe16(&tmd_flags) & 0x02)) {
if (!extract_file_hash(src, 0, cnt_offset, getbe32(&fe[i].FileLength),
path, getbe16(&fe[i].ContentID)))
goto out;
} else {
if (!extract_file(src, 0, cnt_offset, getbe32(&fe[i].FileLength),
path, getbe16(&fe[i].ContentID)))
goto out;
}
fclose(src);
src = NULL;
}
}
}
r = EXIT_SUCCESS;
out:
free(tmd);
free(tik);
free(cnt);
free(tmd_path);
free(tik_path);
if (src != NULL)
fclose(src);
return r;
}

View file

@ -1,5 +0,0 @@
#pragma once
typedef void (*ProgressCallback)(int progress);
void set_progress_callback(ProgressCallback cb);
int cdecrypt_main(int argc, char **argv);

View file

@ -1,320 +0,0 @@
/*
* FIPS-180-1 compliant SHA-1 implementation
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is part of mbed TLS (https://tls.mbed.org) and was downloaded from
* https://github.com/Secure-Embedded-Systems/RSA-example/tree/master/library/.
*/
/*
* The SHA-1 standard was published by NIST in 1993.
*
* http://www.itl.nist.gov/fipspubs/fip180-1.htm
*/
#include "sha1.h"
#include <string.h>
/* Implementation that should never be optimized out by the compiler */
static void zeroize(void *v, size_t n) {
volatile uint8_t *p = (uint8_t *)v;
while (n--)
*p++ = 0;
}
/*
* 32-bit integer manipulation macros (big endian)
*/
#ifndef GET_UINT32_BE
#define GET_UINT32_BE(n, b, i) \
{ \
(n) = ((uint32_t)(b)[(i)] << 24) | ((uint32_t)(b)[(i) + 1] << 16) | \
((uint32_t)(b)[(i) + 2] << 8) | ((uint32_t)(b)[(i) + 3]); \
}
#endif
#ifndef PUT_UINT32_BE
#define PUT_UINT32_BE(n, b, i) \
{ \
(b)[(i)] = (uint8_t)((n) >> 24); \
(b)[(i) + 1] = (uint8_t)((n) >> 16); \
(b)[(i) + 2] = (uint8_t)((n) >> 8); \
(b)[(i) + 3] = (uint8_t)((n)); \
}
#endif
void sha1_init(sha1_context *ctx) { memset(ctx, 0, sizeof(sha1_context)); }
void sha1_free(sha1_context *ctx) {
if (ctx == NULL)
return;
zeroize(ctx, sizeof(sha1_context));
}
void sha1_clone(sha1_context *dst, const sha1_context *src) { *dst = *src; }
/*
* SHA-1 context setup
*/
void sha1_starts(sha1_context *ctx) {
ctx->total[0] = 0;
ctx->total[1] = 0;
ctx->state[0] = 0x67452301;
ctx->state[1] = 0xEFCDAB89;
ctx->state[2] = 0x98BADCFE;
ctx->state[3] = 0x10325476;
ctx->state[4] = 0xC3D2E1F0;
}
void sha1_process(sha1_context *ctx, const uint8_t data[64]) {
uint32_t temp, W[16], A, B, C, D, E;
GET_UINT32_BE(W[0], data, 0);
GET_UINT32_BE(W[1], data, 4);
GET_UINT32_BE(W[2], data, 8);
GET_UINT32_BE(W[3], data, 12);
GET_UINT32_BE(W[4], data, 16);
GET_UINT32_BE(W[5], data, 20);
GET_UINT32_BE(W[6], data, 24);
GET_UINT32_BE(W[7], data, 28);
GET_UINT32_BE(W[8], data, 32);
GET_UINT32_BE(W[9], data, 36);
GET_UINT32_BE(W[10], data, 40);
GET_UINT32_BE(W[11], data, 44);
GET_UINT32_BE(W[12], data, 48);
GET_UINT32_BE(W[13], data, 52);
GET_UINT32_BE(W[14], data, 56);
GET_UINT32_BE(W[15], data, 60);
#define S(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
#define R(t) \
(temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ W[(t - 14) & 0x0F] ^ \
W[t & 0x0F], \
(W[t & 0x0F] = S(temp, 1)))
#define P(a, b, c, d, e, x) \
{ \
e += S(a, 5) + F(b, c, d) + K + x; \
b = S(b, 30); \
}
A = ctx->state[0];
B = ctx->state[1];
C = ctx->state[2];
D = ctx->state[3];
E = ctx->state[4];
#define F(x, y, z) (z ^ (x & (y ^ z)))
#define K 0x5A827999
P(A, B, C, D, E, W[0]);
P(E, A, B, C, D, W[1]);
P(D, E, A, B, C, W[2]);
P(C, D, E, A, B, W[3]);
P(B, C, D, E, A, W[4]);
P(A, B, C, D, E, W[5]);
P(E, A, B, C, D, W[6]);
P(D, E, A, B, C, W[7]);
P(C, D, E, A, B, W[8]);
P(B, C, D, E, A, W[9]);
P(A, B, C, D, E, W[10]);
P(E, A, B, C, D, W[11]);
P(D, E, A, B, C, W[12]);
P(C, D, E, A, B, W[13]);
P(B, C, D, E, A, W[14]);
P(A, B, C, D, E, W[15]);
P(E, A, B, C, D, R(16));
P(D, E, A, B, C, R(17));
P(C, D, E, A, B, R(18));
P(B, C, D, E, A, R(19));
#undef K
#undef F
#define F(x, y, z) (x ^ y ^ z)
#define K 0x6ED9EBA1
P(A, B, C, D, E, R(20));
P(E, A, B, C, D, R(21));
P(D, E, A, B, C, R(22));
P(C, D, E, A, B, R(23));
P(B, C, D, E, A, R(24));
P(A, B, C, D, E, R(25));
P(E, A, B, C, D, R(26));
P(D, E, A, B, C, R(27));
P(C, D, E, A, B, R(28));
P(B, C, D, E, A, R(29));
P(A, B, C, D, E, R(30));
P(E, A, B, C, D, R(31));
P(D, E, A, B, C, R(32));
P(C, D, E, A, B, R(33));
P(B, C, D, E, A, R(34));
P(A, B, C, D, E, R(35));
P(E, A, B, C, D, R(36));
P(D, E, A, B, C, R(37));
P(C, D, E, A, B, R(38));
P(B, C, D, E, A, R(39));
#undef K
#undef F
#define F(x, y, z) ((x & y) | (z & (x | y)))
#define K 0x8F1BBCDC
P(A, B, C, D, E, R(40));
P(E, A, B, C, D, R(41));
P(D, E, A, B, C, R(42));
P(C, D, E, A, B, R(43));
P(B, C, D, E, A, R(44));
P(A, B, C, D, E, R(45));
P(E, A, B, C, D, R(46));
P(D, E, A, B, C, R(47));
P(C, D, E, A, B, R(48));
P(B, C, D, E, A, R(49));
P(A, B, C, D, E, R(50));
P(E, A, B, C, D, R(51));
P(D, E, A, B, C, R(52));
P(C, D, E, A, B, R(53));
P(B, C, D, E, A, R(54));
P(A, B, C, D, E, R(55));
P(E, A, B, C, D, R(56));
P(D, E, A, B, C, R(57));
P(C, D, E, A, B, R(58));
P(B, C, D, E, A, R(59));
#undef K
#undef F
#define F(x, y, z) (x ^ y ^ z)
#define K 0xCA62C1D6
P(A, B, C, D, E, R(60));
P(E, A, B, C, D, R(61));
P(D, E, A, B, C, R(62));
P(C, D, E, A, B, R(63));
P(B, C, D, E, A, R(64));
P(A, B, C, D, E, R(65));
P(E, A, B, C, D, R(66));
P(D, E, A, B, C, R(67));
P(C, D, E, A, B, R(68));
P(B, C, D, E, A, R(69));
P(A, B, C, D, E, R(70));
P(E, A, B, C, D, R(71));
P(D, E, A, B, C, R(72));
P(C, D, E, A, B, R(73));
P(B, C, D, E, A, R(74));
P(A, B, C, D, E, R(75));
P(E, A, B, C, D, R(76));
P(D, E, A, B, C, R(77));
P(C, D, E, A, B, R(78));
P(B, C, D, E, A, R(79));
#undef K
#undef F
ctx->state[0] += A;
ctx->state[1] += B;
ctx->state[2] += C;
ctx->state[3] += D;
ctx->state[4] += E;
}
/*
* SHA-1 process buffer
*/
void sha1_update(sha1_context *ctx, const uint8_t *input, size_t ilen) {
size_t fill;
uint32_t left;
if (ilen == 0)
return;
left = ctx->total[0] & 0x3F;
fill = 64 - left;
ctx->total[0] += (uint32_t)ilen;
ctx->total[0] &= 0xFFFFFFFF;
if (ctx->total[0] < (uint32_t)ilen)
ctx->total[1]++;
if (left && ilen >= fill) {
memcpy((void *)(ctx->buffer + left), input, fill);
sha1_process(ctx, ctx->buffer);
input += fill;
ilen -= fill;
left = 0;
}
while (ilen >= 64) {
sha1_process(ctx, input);
input += 64;
ilen -= 64;
}
if (ilen > 0)
memcpy((void *)(ctx->buffer + left), input, ilen);
}
static const uint8_t sha1_padding[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/*
* SHA-1 final digest
*/
void sha1_finish(sha1_context *ctx, uint8_t output[SHA_DIGEST_LENGTH]) {
uint32_t last, padn;
uint32_t high, low;
uint8_t msglen[8];
high = (ctx->total[0] >> 29) | (ctx->total[1] << 3);
low = (ctx->total[0] << 3);
PUT_UINT32_BE(high, msglen, 0);
PUT_UINT32_BE(low, msglen, 4);
last = ctx->total[0] & 0x3F;
padn = (last < 56) ? (56 - last) : (120 - last);
sha1_update(ctx, sha1_padding, padn);
sha1_update(ctx, msglen, 8);
PUT_UINT32_BE(ctx->state[0], output, 0);
PUT_UINT32_BE(ctx->state[1], output, 4);
PUT_UINT32_BE(ctx->state[2], output, 8);
PUT_UINT32_BE(ctx->state[3], output, 12);
PUT_UINT32_BE(ctx->state[4], output, 16);
}
/*
* output = SHA-1( input buffer )
*/
void sha1(const uint8_t *input, size_t ilen,
uint8_t output[SHA_DIGEST_LENGTH]) {
sha1_context ctx;
sha1_init(&ctx);
sha1_starts(&ctx);
sha1_update(&ctx, input, ilen);
sha1_finish(&ctx, output);
sha1_free(&ctx);
}

View file

@ -1,107 +0,0 @@
/**
* \file sha1.h
*
* \brief SHA-1 cryptographic hash function
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is part of mbed TLS (https://tls.mbed.org) and was downloaded from
* https://github.com/Secure-Embedded-Systems/RSA-example/tree/master/include/.
*/
#ifndef SHA1_H
#define SHA1_H
#include <stddef.h>
#include <stdint.h>
#define SHA_DIGEST_LENGTH 20
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief SHA-1 context structure
*/
typedef struct {
uint32_t total[2]; /*!< number of bytes processed */
uint32_t state[5]; /*!< intermediate digest state */
uint8_t buffer[64]; /*!< data block being processed */
} sha1_context;
/**
* \brief Initialize SHA-1 context
*
* \param ctx SHA-1 context to be initialized
*/
void sha1_init(sha1_context *ctx);
/**
* \brief Clear SHA-1 context
*
* \param ctx SHA-1 context to be cleared
*/
void sha1_free(sha1_context *ctx);
/**
* \brief Clone (the state of) a SHA-1 context
*
* \param dst The destination context
* \param src The context to be cloned
*/
void sha1_clone(sha1_context *dst, const sha1_context *src);
/**
* \brief SHA-1 context setup
*
* \param ctx context to be initialized
*/
void sha1_starts(sha1_context *ctx);
/**
* \brief SHA-1 process buffer
*
* \param ctx SHA-1 context
* \param input buffer holding the data
* \param ilen length of the input data
*/
void sha1_update(sha1_context *ctx, const uint8_t *input, size_t ilen);
/**
* \brief SHA-1 final digest
*
* \param ctx SHA-1 context
* \param output SHA-1 checksum result
*/
void sha1_finish(sha1_context *ctx, uint8_t output[SHA_DIGEST_LENGTH]);
/* Internal use */
void sha1_process(sha1_context *ctx, const uint8_t data[64]);
/**
* \brief Output = SHA-1( input buffer )
*
* \param input buffer holding the data
* \param ilen length of the input data
* \param output SHA-1 checksum result
*/
void sha1(const uint8_t *input, size_t ilen, uint8_t output[SHA_DIGEST_LENGTH]);
#ifdef __cplusplus
}
#endif
#endif /* sha1.h */

View file

@ -1,149 +0,0 @@
/*
UTF-8 handling for Gust (Koei/Tecmo) PC games tools
Copyright © 2019 VitaSmith
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef _DEBUG
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#include <stdlib.h>
#define DUMP_LEAKS _CrtDumpMemoryLeaks()
#else
#define DUMP_LEAKS
#endif
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <wchar.h>
#pragma once
#if defined(_WIN32)
#include <windows.h>
#define stat64 _stat64
#define stat64_t stat64
static __inline char *utf16_to_utf8(const wchar_t *str16) {
char *str8 = NULL;
int str8_size = 0;
if (str16[0] == 0)
return calloc(1, 1);
if (((str8_size = str8_size = WideCharToMultiByte(CP_UTF8, 0, str16, -1, NULL,
0, NULL, NULL)) <= 1) ||
((str8 = (char *)calloc(str8_size, 1)) == NULL))
return NULL;
if (WideCharToMultiByte(CP_UTF8, 0, str16, -1, str8, str8_size, NULL, NULL) !=
str8_size) {
free(str8);
return NULL;
}
return str8;
}
static __inline wchar_t *utf8_to_utf16(const char *str8) {
wchar_t *str16 = NULL;
int str16_size = 0;
if (str8 == NULL)
return NULL;
if (str8[0] == 0)
return calloc(1, sizeof(wchar_t));
if (((str16_size = MultiByteToWideChar(CP_UTF8, 0, str8, -1, NULL, 0)) <=
1) ||
((str16 = (wchar_t *)calloc(str16_size, sizeof(wchar_t))) == NULL))
return NULL;
if (MultiByteToWideChar(CP_UTF8, 0, str8, -1, str16, str16_size) !=
str16_size) {
free(str16);
return NULL;
}
return str16;
}
static __inline FILE *fopen_utf8(const char *filename, const char *mode) {
FILE *r = NULL;
wchar_t *filename16 = utf8_to_utf16(filename);
wchar_t *mode16 = utf8_to_utf16(mode);
_wfopen_s(&r, filename16, mode16);
free(filename16);
free(mode16);
return r;
}
static __inline int rename_utf8(const char *oldname, const char *newname) {
wchar_t *oldname16 = utf8_to_utf16(oldname);
wchar_t *newname16 = utf8_to_utf16(newname);
int r = _wrename(oldname16, newname16);
free(oldname16);
free(newname16);
return r;
}
static __inline int stat64_utf8(const char *path, struct stat64 *buffer) {
int r;
wchar_t *path16 = utf8_to_utf16(path);
r = _wstat64(path16, buffer);
free(path16);
return r;
}
static __inline BOOL CreateDirectory_utf8(const char *path,
LPSECURITY_ATTRIBUTES attrs) {
BOOL r;
wchar_t *path16 = utf8_to_utf16(path);
r = CreateDirectoryW(path16, attrs);
free(path16);
return r;
}
#define CALL_MAIN \
int wmain(int argc, wchar_t **argv16) { \
SetConsoleOutputCP(CP_UTF8); \
char **argv = calloc(argc, sizeof(char *)); \
if (argv == NULL) \
return EXIT_FAILURE; \
for (int i = 0; i < argc; i++) \
argv[i] = utf16_to_utf8(argv16[i]); \
int r = main_utf8(argc, argv); \
for (int i = 0; i < argc; i++) \
free(argv[i]); \
free(argv); \
DUMP_LEAKS; \
return r; \
}
#else
#define fopen_utf8 fopen
#define rename_utf8 rename
#if defined(__APPLE__)
#define stat64_utf8 stat
#define stat64_t stat
#else
#define stat64_utf8 stat64
#define stat64_t stat64
#endif
#define CALL_MAIN \
int main(int argc, char **argv) { return main_utf8(argc, argv); }
#endif

View file

@ -1,242 +0,0 @@
/*
Common code for Gust (Koei/Tecmo) PC games tools
Copyright © 2019-2021 VitaSmith
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utf8.h"
#include "util.h"
bool create_path(char *path) {
bool result = true;
struct stat64_t st;
#if defined(_WIN32)
// Ignore Windows drive names
if ((strlen(path) == 2) && (path[1] == ':'))
return true;
#endif
if (stat64_utf8(path, &st) != 0) {
// Directory doesn't exist, create it
size_t pos = 0;
for (size_t n = strlen(path); n > 0; n--) {
if (path[n] == PATH_SEP) {
while ((n > 0) && (path[--n] == PATH_SEP))
;
pos = n + 1;
break;
}
}
if (pos > 0) {
// Create parent dirs
path[pos] = 0;
char *new_path = malloc(strlen(path) + 1);
if (new_path == NULL) {
fprintf(stderr, "ERROR: Can't allocate path\n");
return false;
}
strcpy(new_path, path);
result = create_path(new_path);
free(new_path);
path[pos] = PATH_SEP;
}
// Create node
if (result)
result = CREATE_DIR(path);
} else if (!S_ISDIR(st.st_mode)) {
fprintf(stderr, "ERROR: '%s' exists but isn't a directory\n", path);
return false;
}
return result;
}
// dirname/basename, that *PRESERVE* the string parameter.
// Note that these calls are not concurrent, meaning that you MUST be done
// using the returned string from a previous call before invoking again.
#if defined(_WIN32)
char *_basename_win32(const char *path, bool remove_extension) {
static char basename[128];
static char ext[64];
ext[0] = 0;
_splitpath_s(path, NULL, 0, NULL, 0, basename, sizeof(basename), ext,
sizeof(ext));
if ((ext[0] != 0) && !remove_extension)
strncat(basename, ext, sizeof(basename) - strlen(basename));
return basename;
}
// This call should behave pretty similar to UNIX' dirname
char *_dirname_win32(const char *path) {
static char dir[PATH_MAX];
static char drive[4];
int found_sep = 0;
memset(drive, 0, sizeof(drive));
_splitpath_s(path, drive, sizeof(drive), dir, sizeof(dir) - 3, NULL, 0, NULL,
0);
// Only deal with drives that are one letter
drive[2] = 0;
drive[3] = 0;
if (drive[1] != ':')
drive[0] = 0;
// Removing trailing path separators
for (int32_t n = (int32_t)strlen(dir) - 1;
(n > 0) && ((dir[n] == '/') || (dir[n] == '\\')); n--) {
dir[n] = 0;
found_sep++;
}
if (dir[0] == 0) {
if (drive[0] == 0)
return found_sep ? "\\" : ".";
drive[2] = '\\';
return drive;
}
if (drive[0] != 0) {
// Add the drive
memmove(&dir[2], dir, strlen(dir) + 1);
memcpy(dir, drive, strlen(drive));
dir[2] = '\\';
}
return dir;
}
#else
char *_basename_unix(const char *path) {
static char path_copy[PATH_MAX];
strncpy(path_copy, path, sizeof(path_copy));
path_copy[PATH_MAX - 1] = 0;
return basename(path_copy);
}
char *_dirname_unix(const char *path) {
static char path_copy[PATH_MAX];
strncpy(path_copy, path, sizeof(path_copy));
path_copy[PATH_MAX - 1] = 0;
return dirname(path_copy);
}
#endif
bool is_file(const char *path) {
struct stat64_t st;
return (stat64_utf8(path, &st) == 0) && S_ISREG(st.st_mode);
}
bool is_directory(const char *path) {
struct stat64_t st;
return (stat64_utf8(path, &st) == 0) && S_ISDIR(st.st_mode);
}
char *change_extension(const char *path, const char *extension) {
static char new_path[PATH_MAX];
strncpy(new_path, _basename((char *)path), sizeof(new_path) - 1);
for (size_t i = 0; i < sizeof(new_path); i++) {
if (new_path[i] == '.')
new_path[i] = 0;
}
strncat(new_path, extension, sizeof(new_path) - strlen(new_path) - 1);
return new_path;
}
size_t get_trailing_slash(const char *path) {
size_t i;
if ((path == NULL) || (path[0] == 0))
return 0;
for (i = strlen(path) - 1; (i > 0) && ((path[i] != '/') && (path[i] != '\\'));
i--)
;
return (i == 0) ? 0 : i + 1;
}
uint32_t read_file_max(const char *path, uint8_t **buf, uint32_t max_size) {
FILE *file = fopen_utf8(path, "rb");
if (file == NULL) {
fprintf(stderr, "ERROR: Can't open '%s'\n", path);
return 0;
}
fseek(file, 0L, SEEK_END);
uint32_t size = (uint32_t)ftell(file);
fseek(file, 0L, SEEK_SET);
if (max_size != 0)
size = min(size, max_size);
*buf = calloc(size, 1);
if (*buf == NULL) {
size = 0;
goto out;
}
if (fread(*buf, 1, size, file) != size) {
fprintf(stderr, "ERROR: Can't read '%s'\n", path);
size = 0;
}
out:
fclose(file);
if (size == 0) {
free(*buf);
*buf = NULL;
}
return size;
}
uint64_t get_file_size(const char *path) {
FILE *file = fopen_utf8(path, "rb");
if (file == NULL) {
fprintf(stderr, "ERROR: Can't open '%s'\n", path);
return 0;
}
fseek(file, 0L, SEEK_END);
uint64_t size = ftell64(file);
fclose(file);
return size;
}
void create_backup(const char *path) {
struct stat64_t st;
if (stat64_utf8(path, &st) == 0) {
char *backup_path = malloc(strlen(path) + 5);
if (backup_path == NULL)
return;
strcpy(backup_path, path);
strcat(backup_path, ".bak");
if (stat64_utf8(backup_path, &st) != 0) {
if (rename_utf8(path, backup_path) == 0)
printf("Saved backup as '%s'\n", backup_path);
else
fprintf(stderr, "WARNING: Could not create backup file '%s\n",
backup_path);
}
free(backup_path);
}
}
bool write_file(const uint8_t *buf, const uint32_t size, const char *path,
const bool backup) {
if (backup)
create_backup(path);
FILE *file = fopen_utf8(path, "wb");
if (file == NULL) {
fprintf(stderr, "ERROR: Can't create file '%s'\n", path);
return false;
}
bool r = (fwrite(buf, 1, size, file) == size);
fclose(file);
if (!r)
fprintf(stderr, "ERROR: Can't write file '%s'\n", path);
return r;
}

View file

@ -1,206 +0,0 @@
/*
Common code for Gust (Koei/Tecmo) PC games tools
Copyright © 2019-2021 VitaSmith
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef _DEBUG
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#include <stdlib.h>
#endif
#if !defined(_WIN32)
#include <libgen.h>
#endif
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/types.h>
#pragma once
#define _STRINGIFY(x) #x
#define STRINGIFY(x) _STRINGIFY(x)
#ifndef APP_VERSION
#define APP_VERSION_STR "[DEV VERSION]"
#else
#define APP_VERSION_STR STRINGIFY(APP_VERSION)
#endif
#if defined(_WIN32)
#include <windows.h>
#define ftell64 _ftelli64
#define fseek64 _fseeki64
#if !defined(S_ISDIR)
#define S_ISDIR(ST_MODE) (((ST_MODE)&_S_IFMT) == _S_IFDIR)
#endif
#if !defined(S_ISREG)
#define S_ISREG(ST_MODE) (((ST_MODE)&_S_IFMT) == _S_IFREG)
#endif
#define CREATE_DIR(path) CreateDirectory_utf8(path, NULL)
#define PATH_SEP '\\'
#else
#if defined(__APPLE__)
#define ftell64 ftello
#define fseek64 fseeko
#else
#define ftell64 ftello64
#define fseek64 fseeko64
#endif
#define CREATE_DIR(path) (mkdir(path, 0755) == 0)
#define PATH_SEP '/'
#endif
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif
#ifndef min
#define min(a, b) (((a) < (b)) ? (a) : (b))
#endif
#ifndef max
#define max(a, b) (((a) > (b)) ? (a) : (b))
#endif
#ifndef array_size
#define array_size(a) (sizeof(a) / sizeof(*a))
#endif
#ifndef is_power_of_2
#define is_power_of_2(x) (((x) & ((x)-1)) == 0)
#endif
#if defined(_WIN32)
char *_basename_win32(const char *path, bool remove_extension);
char *_dirname_win32(const char *path);
#define _basename(path) _basename_win32(path, false)
#define _appname(path) _basename_win32(path, true)
#define _dirname(path) _dirname_win32(path)
#else
char *_basename_unix(const char *path);
char *_dirname_unix(const char *path);
#define _basename(path) _basename_unix(path)
#define _appname(path) _basename_unix(path)
#define _dirname(path) _dirname_unix(path)
#endif
#if defined(_MSC_VER)
#include <stdlib.h>
#define bswap_uint16 _byteswap_ushort
#define bswap_uint32 _byteswap_ulong
#define bswap_uint64 _byteswap_uint64
#else
#define bswap_uint16 __builtin_bswap16
#define bswap_uint32 __builtin_bswap32
#define bswap_uint64 __builtin_bswap64
#endif
// Returns the position of the msb. v should be nonzero
static __inline uint32_t find_msb(uint32_t v) {
#if defined(_MSC_VER)
DWORD pos;
_BitScanReverse(&pos, v);
return pos;
#else
return 31 - __builtin_clz(v);
#endif
}
static __inline uint16_t getle16(const void *p) {
return *(const uint16_t *)(const uint8_t *)(p);
}
static __inline void setle16(const void *p, const uint16_t v) {
*((uint16_t *)p) = v;
}
static __inline uint16_t getbe16(const void *p) {
return bswap_uint16(getle16(p));
}
static __inline void setbe16(const void *p, const uint16_t v) {
setle16(p, bswap_uint16(v));
}
static __inline uint32_t getle24(const void *_p) {
uint8_t *p = (uint8_t *)_p;
return p[0] | (p[1] << 8) | (p[2] << 16);
}
static __inline void setle24(const void *_p, const uint32_t v) {
uint8_t *p = (uint8_t *)_p;
p[0] = v & 0xff;
p[1] = (v >> 8) & 0xff;
p[2] = (v >> 16) & 0xff;
}
static __inline uint32_t getbe24(const void *_p) {
uint8_t *p = (uint8_t *)_p;
return (p[0] << 16) | (p[1] << 8) | p[2];
}
static __inline void setbe24(const void *_p, const uint32_t v) {
uint8_t *p = (uint8_t *)_p;
p[0] = (v >> 16) & 0xff;
p[1] = (v >> 8) & 0xff;
p[2] = v & 0xff;
}
static __inline uint32_t getle32(const void *p) {
return *(const uint32_t *)(const uint8_t *)(p);
}
static __inline void setle32(const void *p, const uint32_t v) {
*((uint32_t *)p) = v;
}
static __inline uint32_t getbe32(const void *p) {
return bswap_uint32(getle32(p));
}
static __inline void setbe32(const void *p, const uint32_t v) {
setle32(p, bswap_uint32(v));
}
static __inline uint64_t getle64(const void *p) {
return *(const uint64_t *)(const uint8_t *)(p);
}
static __inline void setle64(const void *p, const uint64_t v) {
*((uint64_t *)p) = v;
}
static __inline uint64_t getbe64(const void *p) {
return bswap_uint64(getle64(p));
}
static __inline void setbe64(const void *p, const uint64_t v) {
setle64(p, bswap_uint64(v));
}
bool create_path(char *path);
char *change_extension(const char *path, const char *extension);
size_t get_trailing_slash(const char *path);
bool is_file(const char *path);
bool is_directory(const char *path);
uint32_t read_file_max(const char *path, uint8_t **buf, uint32_t max_size);
#define read_file(path, buf) read_file_max(path, buf, 0)
uint64_t get_file_size(const char *path);
void create_backup(const char *path);
bool write_file(const uint8_t *buf, const uint32_t size, const char *path,
const bool backup);

View file

@ -1,74 +1,581 @@
package wiiudownloader
/*
#cgo CFLAGS: -I${SRCDIR}/cdecrypt
#cgo LDFLAGS: -Wl,-rpath,${SRCDIR}
#cgo LDFLAGS: -L${SRCDIR}
#cgo LDFLAGS: -lcdecrypt
#include <cdecrypt.h>
#include <ctype.h>
#include <stdlib.h>
// Declare a separate C function that calls the Go function progressCallback
extern void callProgressCallback(int progress);
*/
import "C"
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/sha1"
"encoding/binary"
"encoding/hex"
"errors"
"time"
"unsafe"
"golang.org/x/sync/errgroup"
"fmt"
"io"
"os"
"path/filepath"
"reflect"
)
//export callProgressCallback
func callProgressCallback(progress C.int) {
progressChan <- int(progress)
const (
BLOCK_SIZE = 0x8000
BLOCK_SIZE_HASHED = 0x10000
HASH_BLOCK_SIZE = 0xFC00
HASHES_SIZE = 0x0400
MAX_LEVELS = 0x10
)
const READ_SIZE = 8 * 1024 * 1024
type Content struct {
ID uint32
Index []byte
Type uint16
Size uint64
Hash []byte
CIDStr string
}
var progressChan chan int
func DecryptContents(path string, progress ProgressReporter, deleteEncryptedContents bool) error {
progressChan = make(chan int)
errGroup := errgroup.Group{}
errGroup.Go(func() error {
return runDecryption(path, deleteEncryptedContents)
})
for progressInt := range progressChan {
if progressInt > 0 {
progress.UpdateDecryptionProgress(float64(progressInt) / 100)
}
time.Sleep(time.Millisecond * 10)
}
return errGroup.Wait()
type FEntry struct {
Type byte // 0 = file, 1 = directory
NameOffset uint32 // 3 bytes
Offset uint32 // 4 bytes
Length uint32 // 4 bytes
Flags uint16 // 2 bytes
ContentID uint16 // 2 bytes
}
func runDecryption(path string, deleteEncryptedContents bool) error {
defer close(progressChan)
argv := []*C.char{
C.CString("WiiUDownloader"),
C.CString(path),
type FSTData struct {
FSTReader *bytes.Reader
EntryCount uint32
Entries uint32
NamesOffset uint32
FSTEntries []FEntry
}
func extractFileHash(src *os.File, partDataOffset uint64, fileOffset uint64, size uint64, path string, contentId uint16, cipherHashTree cipher.Block) error {
encryptedContent := make([]byte, BLOCK_SIZE_HASHED)
decryptedContent := make([]byte, BLOCK_SIZE_HASHED)
hashes := make([]byte, HASHES_SIZE)
writeSize := HASH_BLOCK_SIZE
blockNumber := (fileOffset / HASH_BLOCK_SIZE) & 0x0F
dst, err := os.Create(path)
if err != nil {
return fmt.Errorf("could not create '%s': %w", path, err)
}
defer func() {
for _, arg := range argv {
C.free(unsafe.Pointer(arg))
defer dst.Close()
roffset := fileOffset / HASH_BLOCK_SIZE * BLOCK_SIZE_HASHED
soffset := fileOffset - (fileOffset / HASH_BLOCK_SIZE * HASH_BLOCK_SIZE)
if soffset+size > uint64(writeSize) {
writeSize = writeSize - int(soffset)
}
_, err = src.Seek(int64(partDataOffset+roffset), io.SeekStart)
if err != nil {
return err
}
for size > 0 {
if uint64(writeSize) > size {
writeSize = int(size)
}
}()
// Register the C callback function with C
C.set_progress_callback(C.ProgressCallback(C.callProgressCallback))
if _, err := io.ReadFull(src, encryptedContent); err != nil {
return fmt.Errorf("could not read %d bytes from '%s': %w", BLOCK_SIZE_HASHED, path, err)
}
if int(C.cdecrypt_main(2, (**C.char)(unsafe.Pointer(&argv[0])))) != 0 {
return errors.New("decryption failed")
}
iv := make([]byte, aes.BlockSize)
iv[1] = byte(contentId)
cipher.NewCBCDecrypter(cipherHashTree, iv).CryptBlocks(hashes, encryptedContent[:HASHES_SIZE])
if deleteEncryptedContents {
doDeleteEncryptedContents(path)
h0Hash := hashes[0x14*blockNumber : 0x14*blockNumber+sha1.Size]
iv = hashes[0x14*blockNumber : 0x14*blockNumber+aes.BlockSize]
if blockNumber == 0 {
iv[1] ^= byte(contentId)
}
cipher.NewCBCDecrypter(cipherHashTree, iv).CryptBlocks(decryptedContent, encryptedContent[HASHES_SIZE:])
hash := sha1.Sum(decryptedContent[:HASH_BLOCK_SIZE])
if !reflect.DeepEqual(hash[:], h0Hash) {
return errors.New("h0 hash mismatch")
}
size -= uint64(writeSize)
_, err = dst.Write(decryptedContent[soffset : soffset+uint64(writeSize)])
if err != nil {
return err
}
blockNumber++
if blockNumber >= 16 {
blockNumber = 0
}
if soffset != 0 {
writeSize = HASH_BLOCK_SIZE
soffset = 0
}
}
return nil
}
func extractFile(src *os.File, part_data_offset uint64, file_offset uint64, size uint64, path string, content_id uint16, cipherHashTree cipher.Block) error {
enc := make([]byte, BLOCK_SIZE)
dec := make([]byte, BLOCK_SIZE)
iv := make([]byte, 16)
roffset := file_offset / BLOCK_SIZE * BLOCK_SIZE
soffset := file_offset - (file_offset / BLOCK_SIZE * BLOCK_SIZE)
dst, err := os.Create(path)
if err != nil {
return fmt.Errorf("could not create '%s': %w", path, err)
}
defer dst.Close()
iv[1] = byte(content_id)
write_size := BLOCK_SIZE
if soffset+size > uint64(write_size) {
write_size = write_size - int(soffset)
}
_, err = src.Seek(int64(part_data_offset+roffset), io.SeekStart)
if err != nil {
return err
}
for size > 0 {
if uint64(write_size) > size {
write_size = int(size)
}
if _, err := io.ReadFull(src, enc); err != nil {
return fmt.Errorf("could not read %d bytes from '%s': %w", BLOCK_SIZE, path, err)
}
mode := cipher.NewCBCDecrypter(cipherHashTree, iv)
mode.CryptBlocks(dec, enc)
size -= uint64(write_size)
_, err = dst.Write(dec[soffset : soffset+uint64(write_size)])
if err != nil {
return err
}
if soffset != 0 {
write_size = BLOCK_SIZE
soffset = 0
}
}
return nil
}
func parseFSTEntry(fst *FSTData) error {
for i := uint32(0); i < fst.Entries; i++ {
entry := FEntry{}
entry.Type = readByte(fst.FSTReader)
entry.NameOffset = uint32(read3BytesBE(fst.FSTReader))
entry.Offset = readInt(fst.FSTReader, 4)
entry.Length = readInt(fst.FSTReader, 4)
entry.Flags = readInt16(fst.FSTReader, 2)
entry.ContentID = readInt16(fst.FSTReader, 2)
fst.FSTEntries = append(fst.FSTEntries, entry)
}
return nil
}
func parseFST(fst *FSTData) {
fst.FSTReader.Seek(0x8, io.SeekStart)
fst.EntryCount = readInt(fst.FSTReader, 4)
fst.FSTReader.Seek(int64(0x20+fst.EntryCount*0x20+8), io.SeekStart)
fst.Entries = readInt(fst.FSTReader, 4)
fst.NamesOffset = 0x20 + fst.EntryCount*0x20 + fst.Entries*0x10
fst.FSTReader.Seek(4, io.SeekCurrent)
parseFSTEntry(fst)
}
func readByte(f io.ReadSeeker) byte {
buf := make([]byte, 1)
n, err := f.Read(buf)
if err != nil {
panic(err)
}
if n < 1 {
panic(io.ErrUnexpectedEOF)
}
return buf[0]
}
func readInt(f io.ReadSeeker, s int) uint32 {
bufSize := 4 // Buffer size is always 4 for uint32
buf := make([]byte, bufSize)
n, err := f.Read(buf[:s])
if err != nil {
panic(err)
}
if n < s {
// If we didn't read the expected number of bytes, seek back to the
// previous position in the file and return an error.
if _, err := f.Seek(int64(-n), io.SeekCurrent); err != nil {
panic(err)
}
panic(io.ErrUnexpectedEOF)
}
return binary.BigEndian.Uint32(buf)
}
func readInt16(f io.ReadSeeker, s int) uint16 {
bufSize := 2 // Buffer size is always 2 for uint16
buf := make([]byte, bufSize)
n, err := f.Read(buf[:s])
if err != nil {
panic(err)
}
if n < s {
// If we didn't read the expected number of bytes, seek back to the
// previous position in the file and return an error.
if _, err := f.Seek(int64(-n), io.SeekCurrent); err != nil {
panic(err)
}
panic(io.ErrUnexpectedEOF)
}
return binary.BigEndian.Uint16(buf)
}
func readString(f io.ReadSeeker) string {
buf := []byte{}
for {
char := make([]byte, 1)
f.Read(char)
if char[0] == byte(0) || len(char) == 0 {
return string(buf)
}
buf = append(buf, char[0])
}
}
func read3BytesBE(f io.ReadSeeker) int {
b := make([]byte, 3)
f.Read(b)
return int(uint(b[2]) | uint(b[1])<<8 | uint(b[0])<<16)
}
func decryptContentToBuffer(encryptedFile *os.File, decryptedBuffer *bytes.Buffer, cipherHashTree cipher.Block, content Content) error {
hasHashTree := content.Type&2 != 0
encryptedStat, err := encryptedFile.Stat()
if err != nil {
return err
}
encryptedSize := encryptedStat.Size()
path := filepath.Dir(encryptedFile.Name())
if hasHashTree { // if has a hash tree
chunkCount := encryptedSize / 0x10000
h3Data, err := os.ReadFile(filepath.Join(path, fmt.Sprintf("%s.h3", content.CIDStr)))
if err != nil {
return err
}
h3BytesSHASum := sha1.Sum(h3Data)
if hex.EncodeToString(h3BytesSHASum[:]) != hex.EncodeToString(content.Hash) {
return errors.New("H3 Hash mismatch")
}
h0HashNum := int64(0)
h1HashNum := int64(0)
h2HashNum := int64(0)
h3HashNum := int64(0)
hashes := make([]byte, 0x400)
buffer := make([]byte, 0x400)
for chunkNum := int64(0); chunkNum < chunkCount; chunkNum++ {
encryptedFile.Read(buffer)
cipher.NewCBCDecrypter(cipherHashTree, make([]byte, aes.BlockSize)).CryptBlocks(hashes, buffer)
h0Hashes := hashes[0:0x140]
h1Hashes := hashes[0x140:0x280]
h2Hashes := hashes[0x280:0x3c0]
h0Hash := h0Hashes[(h0HashNum * 0x14):((h0HashNum + 1) * 0x14)]
h1Hash := h1Hashes[(h1HashNum * 0x14):((h1HashNum + 1) * 0x14)]
h2Hash := h2Hashes[(h2HashNum * 0x14):((h2HashNum + 1) * 0x14)]
h3Hash := h3Data[(h3HashNum * 0x14):((h3HashNum + 1) * 0x14)]
h0HashesHash := sha1.Sum(h0Hashes)
h1HashesHash := sha1.Sum(h1Hashes)
h2HashesHash := sha1.Sum(h2Hashes)
if !reflect.DeepEqual(h0HashesHash[:], h1Hash) {
return errors.New("h0 Hashes Hash mismatch")
}
if !reflect.DeepEqual(h1HashesHash[:], h2Hash) {
return errors.New("h1 Hashes Hash mismatch")
}
if !reflect.DeepEqual(h2HashesHash[:], h3Hash) {
return errors.New("h2 Hashes Hash mismatch")
}
decryptedData := make([]byte, 0xFC00)
encryptedFile.Read(decryptedData)
cipher.NewCBCDecrypter(cipherHashTree, h0Hash[:16]).CryptBlocks(decryptedData, decryptedData)
decryptedDataHash := sha1.Sum(decryptedData)
if !reflect.DeepEqual(decryptedDataHash[:], h0Hash) {
return errors.New("data block hash invalid")
}
_, err = decryptedBuffer.Write(hashes)
if err != nil {
return err
}
_, err = decryptedBuffer.Write(decryptedData)
if err != nil {
return err
}
h0HashNum++
if h0HashNum >= 16 {
h0HashNum = 0
h1HashNum++
}
if h1HashNum >= 16 {
h1HashNum = 0
h2HashNum++
}
if h2HashNum >= 16 {
h2HashNum = 0
h3HashNum++
}
}
} else {
cipherContent := cipher.NewCBCDecrypter(cipherHashTree, append(content.Index, make([]byte, 14)...))
contentHash := sha1.New()
left := content.Size
leftHash := content.Size
for i := 0; i <= int(content.Size/READ_SIZE)+1; i++ {
toRead := min(READ_SIZE, left)
toReadHash := min(READ_SIZE, leftHash)
encryptedContent := make([]byte, toRead)
_, err = io.ReadFull(encryptedFile, encryptedContent)
if err != nil {
return err
}
decryptedContent := make([]byte, len(encryptedContent))
cipherContent.CryptBlocks(decryptedContent, encryptedContent)
contentHash.Write(decryptedContent[:toReadHash])
_, err = decryptedBuffer.Write(decryptedContent)
if err != nil {
return err
}
left -= toRead
leftHash -= toRead
if left == 0 {
break
}
}
if !reflect.DeepEqual(content.Hash, contentHash.Sum(nil)) {
return errors.New("content hash mismatch")
}
}
return nil
}
func DecryptContents(path string, progressReporter ProgressReporter, deleteEncryptedContents bool) error {
tmdPath := filepath.Join(path, "title.tmd")
if _, err := os.Stat(tmdPath); os.IsNotExist(err) {
return err
}
// find title id and content id
var titleID []byte
var contentCount uint16
tmd, err := os.Open(tmdPath)
if err != nil {
return err
}
defer tmd.Close()
tmd.Seek(0x18C, io.SeekStart)
titleID = make([]byte, 8)
if _, err := io.ReadFull(tmd, titleID); err != nil {
return err
}
tmd.Seek(0x1DE, io.SeekStart)
if err := binary.Read(tmd, binary.BigEndian, &contentCount); err != nil {
return err
}
tmd.Seek(0x204, io.SeekStart)
tmdIndex := make([]byte, 2)
if _, err := io.ReadFull(tmd, tmdIndex); err != nil {
return err
}
contents := make([]Content, contentCount)
for c := uint16(0); c < contentCount; c++ {
offset := 2820 + (48 * c)
tmd.Seek(int64(offset), io.SeekStart)
if err := binary.Read(tmd, binary.BigEndian, &contents[c].ID); err != nil {
return err
}
tmd.Seek(0xB08+(0x30*int64(c)), io.SeekStart)
contents[c].Index = make([]byte, 2)
if _, err := io.ReadFull(tmd, contents[c].Index); err != nil {
return err
}
tmd.Seek(0xB0A+(0x30*int64(c)), io.SeekStart)
if err := binary.Read(tmd, binary.BigEndian, &contents[c].Type); err != nil {
return err
}
tmd.Seek(0xB0C+(0x30*int64(c)), io.SeekStart)
if err := binary.Read(tmd, binary.BigEndian, &contents[c].Size); err != nil {
return err
}
tmd.Seek(0xB14+(0x30*int64(c)), io.SeekStart)
contents[c].Hash = make([]byte, 0x14)
if _, err := io.ReadFull(tmd, contents[c].Hash); err != nil {
return err
}
contents[c].CIDStr = fmt.Sprintf("%08X", contents[c].ID)
_, err := os.Stat(filepath.Join(path, contents[c].CIDStr+".app"))
if err != nil {
contents[c].CIDStr = fmt.Sprintf("%08x", contents[c].ID)
_, err = os.Stat(filepath.Join(path, contents[c].CIDStr+".app"))
if err != nil {
return errors.New("content not found")
}
}
}
// Find the encrypted titlekey
var encryptedTitleKey []byte
ticketPath := filepath.Join(path, "title.tik")
if _, err := os.Stat(ticketPath); err == nil {
cetk, err := os.Open(ticketPath)
if err == nil {
cetk.Seek(0x1BF, 0)
encryptedTitleKey = make([]byte, 0x10)
cetk.Read(encryptedTitleKey)
cetk.Close()
}
}
c, err := aes.NewCipher(commonKey)
if err != nil {
return err
}
cbc := cipher.NewCBCDecrypter(c, append(titleID, make([]byte, 8)...))
decryptedTitleKey := make([]byte, len(encryptedTitleKey))
cbc.CryptBlocks(decryptedTitleKey, encryptedTitleKey)
cipherHashTree, err := aes.NewCipher(decryptedTitleKey)
if err != nil {
return fmt.Errorf("failed to create AES cipher: %w", err)
}
fstEncFile, err := os.Open(filepath.Join(path, contents[0].CIDStr+".app"))
if err != nil {
return err
}
defer fstEncFile.Close()
decryptedBuffer := bytes.Buffer{}
if err := decryptContentToBuffer(fstEncFile, &decryptedBuffer, cipherHashTree, contents[0]); err != nil {
return err
}
fst := FSTData{FSTReader: bytes.NewReader(bytes.Clone(decryptedBuffer.Bytes())), FSTEntries: make([]FEntry, 0), EntryCount: 0, Entries: 0, NamesOffset: 0}
parseFST(&fst)
outputPath := path
entry := make([]uint32, 0x10)
lEntry := make([]uint32, 0x10)
level := uint32(0)
for i := uint32(0); i < fst.Entries-1; i++ {
progressReporter.UpdateDecryptionProgress(float64(i) / float64(fst.Entries-1))
if level > 0 {
for (level >= 1) && (lEntry[level-1] == i+1) {
level--
}
}
if fst.FSTEntries[i].Type&1 != 0 {
entry[level] = i
lEntry[level] = fst.FSTEntries[i].Length
level++
if level >= MAX_LEVELS {
return errors.New("level >= MAX_LEVELS")
}
} else {
pathOffset := uint32(0)
outputPath = path
for j := uint32(0); j < level; j++ {
pathOffset = fst.FSTEntries[entry[j]].NameOffset & 0x00FFFFFF
fst.FSTReader.Seek(int64(fst.NamesOffset+pathOffset), io.SeekStart)
outputPath = filepath.Join(outputPath, readString(fst.FSTReader))
os.MkdirAll(outputPath, 0755)
}
pathOffset = fst.FSTEntries[i].NameOffset & 0x00FFFFFF
fst.FSTReader.Seek(int64(fst.NamesOffset+pathOffset), io.SeekStart)
outputPath = filepath.Join(outputPath, readString(fst.FSTReader))
contentOffset := uint64(fst.FSTEntries[i].Offset)
if fst.FSTEntries[i].Flags&4 == 0 {
contentOffset <<= 5
}
if fst.FSTEntries[i].Type&0x80 == 0 {
matchingContent := contents[fst.FSTEntries[i].ContentID]
tmdFlags := matchingContent.Type
srcFile, err := os.Open(filepath.Join(path, matchingContent.CIDStr+".app"))
if err != nil {
return err
}
defer srcFile.Close()
if tmdFlags&0x02 != 0 {
err = extractFileHash(srcFile, 0, uint64(fst.FSTEntries[i].Offset), uint64(fst.FSTEntries[i].Length), outputPath, fst.FSTEntries[i].ContentID, cipherHashTree)
} else {
err = extractFile(srcFile, 0, uint64(fst.FSTEntries[i].Offset), uint64(fst.FSTEntries[i].Length), outputPath, fst.FSTEntries[i].ContentID, cipherHashTree)
}
if err != nil {
return err
}
}
}
}
if deleteEncryptedContents {
doDeleteEncryptedContents(path)
}
return nil
}

View file

@ -249,18 +249,17 @@ func DownloadTitle(cancelCtx context.Context, titleID, outputDirectory string, d
return fmt.Errorf("failed to create AES cipher: %w", err)
}
var id uint32
var content contentInfo
var content Content
tmdDataReader := bytes.NewReader(tmdData)
for i := 0; i < int(contentCount); i++ {
offset := 2820 + (48 * i)
tmdDataReader.Seek(int64(offset), 0)
if err := binary.Read(tmdDataReader, binary.BigEndian, &id); err != nil {
if err := binary.Read(tmdDataReader, binary.BigEndian, &content.ID); err != nil {
return err
}
filePath := filepath.Join(outputDir, fmt.Sprintf("%08X.app", id))
if err := downloadFile(cancelCtx, progressReporter, client, fmt.Sprintf("%s/%08X", baseURL, id), filePath, true, buffer); err != nil {
filePath := filepath.Join(outputDir, fmt.Sprintf("%08X.app", content.ID))
if err := downloadFile(cancelCtx, progressReporter, client, fmt.Sprintf("%s/%08X", baseURL, content.ID), filePath, true, buffer); err != nil {
if progressReporter.Cancelled() {
break
}
@ -269,16 +268,15 @@ func DownloadTitle(cancelCtx context.Context, titleID, outputDirectory string, d
progressReporter.AddToTotalDownloaded(int64(contentSizes[i]))
if tmdData[offset+7]&0x2 == 2 {
filePath = filepath.Join(outputDir, fmt.Sprintf("%08X.h3", id))
if err := downloadFile(cancelCtx, progressReporter, client, fmt.Sprintf("%s/%08X.h3", baseURL, id), filePath, true, buffer); err != nil {
filePath = filepath.Join(outputDir, fmt.Sprintf("%08X.h3", content.ID))
if err := downloadFile(cancelCtx, progressReporter, client, fmt.Sprintf("%s/%08X.h3", baseURL, content.ID), filePath, true, buffer); err != nil {
if progressReporter.Cancelled() {
break
}
return err
}
content.Hash = tmdData[offset+16 : offset+0x14]
content.ID = fmt.Sprintf("%08X", id)
content.Size = int64(contentSizes[i])
content.Size = contentSizes[i]
if err := checkContentHashes(outputDirectory, content, cipherHashTree); err != nil {
if progressReporter.Cancelled() {
break

2
go.mod
View file

@ -1,6 +1,6 @@
module github.com/Xpl0itU/WiiUDownloader
go 1.20
go 1.21
require (
github.com/Xpl0itU/dialog v0.0.0-20230805114139-ec888310aded

View file

@ -20,15 +20,5 @@ opener = urllib.request.build_opener(urllib.request.HTTPSHandler(context=ssl_con
opener.addheaders = [("User-agent", "NUSspliBuilder/2.1")]
urllib.request.install_opener(opener)
checkAndDeleteFile("gtitles/gtitles.c")
urllib.request.urlretrieve("https://napi.v10lator.de/db", "gtitles/gtitles.c")
os.system("gcc -c -Wall -fpic -Ofast -pipe -Igtitles -o gtitles/gtitles.o gtitles/gtitles.c")
os.system("ar rcs libgtitles.a gtitles/gtitles.o")
os.system("gcc -shared -o gtitles/libgtitles.so gtitles/gtitles.o")
os.system("gcc -c -Wall -fpic -Ofast -pipe -UNDEBUG -DAES_ROM_TABLES -D_GNU_SOURCE -Icdecrypt -o cdecrypt/aes.o cdecrypt/aes.c")
os.system("gcc -c -Wall -fpic -Ofast -pipe -UNDEBUG -DAES_ROM_TABLES -D_GNU_SOURCE -Icdecrypt -o cdecrypt/cdecrypt.o cdecrypt/cdecrypt.c")
os.system("gcc -c -Wall -fpic -Ofast -pipe -UNDEBUG -DAES_ROM_TABLES -D_GNU_SOURCE -Icdecrypt -o cdecrypt/sha1.o cdecrypt/sha1.c")
os.system("gcc -c -Wall -fpic -Ofast -pipe -UNDEBUG -DAES_ROM_TABLES -D_GNU_SOURCE -Icdecrypt -o cdecrypt/util.o cdecrypt/util.c")
os.system("ar rcs libcdecrypt.a cdecrypt/*.o")
os.system("gcc -shared -o cdecrypt/libcdecrypt.so cdecrypt/*.o")
checkAndDeleteFile("db.go")
urllib.request.urlretrieve("https://napi.v10lator.de/db?t=go", "db.go")

View file

@ -1,17 +1,7 @@
package wiiudownloader
/*
#cgo CFLAGS: -I${SRCDIR}/gtitles
#cgo LDFLAGS: -Wl,-rpath,${SRCDIR}
#cgo LDFLAGS: -L${SRCDIR}
#cgo LDFLAGS: -lgtitles
#include <gtitles.h>
#include <ctype.h>
*/
import "C"
import (
"strconv"
"unsafe"
)
const (
@ -58,31 +48,14 @@ const (
TID_HIGH_UPDATE = 0x0005000E
)
type TitleEntry struct {
Name string
TitleID uint64
Region uint8
key uint8
}
func GetTitleEntries(category uint8) []TitleEntry {
entriesSize := getTitleEntriesSize(category)
entriesSlice := make([]TitleEntry, entriesSize)
cEntries := C.getTitleEntries(C.TITLE_CATEGORY(category))
cSlice := (*[1 << 28]C.TitleEntry)(unsafe.Pointer(cEntries))[:entriesSize:entriesSize]
for i := 0; i < entriesSize; i++ {
entriesSlice[i] = TitleEntry{
Name: C.GoString(cSlice[i].name),
TitleID: uint64(cSlice[i].tid),
Region: uint8(cSlice[i].region),
key: uint8(cSlice[i].key),
titleEntries := make([]TitleEntry, 0)
for _, entry := range titleEntry {
if entry.Category == category {
titleEntries = append(titleEntries, entry)
}
}
return entriesSlice
}
func getTitleEntriesSize(category uint8) int {
return int(C.getTitleEntriesSize(C.TITLE_CATEGORY(category)))
return titleEntries
}
func GetFormattedRegion(region uint8) string {

View file

View file

@ -1,90 +0,0 @@
/***************************************************************************
* This file is part of NUSspli. *
* Copyright (c) 2022 V10lator <v10lator@myway.de> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#pragma once
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum MCPRegion {
MCP_REGION_JAPAN = 0x01,
MCP_REGION_USA = 0x02,
MCP_REGION_EUROPE = 0x04,
MCP_REGION_CHINA = 0x10,
MCP_REGION_KOREA = 0x20,
MCP_REGION_TAIWAN = 0x40,
} MCPRegion;
typedef enum {
TID_HIGH_GAME = 0x00050000,
TID_HIGH_DEMO = 0x00050002,
TID_HIGH_SYSTEM_APP = 0x00050010,
TID_HIGH_SYSTEM_DATA = 0x0005001B,
TID_HIGH_SYSTEM_APPLET = 0x00050030,
TID_HIGH_VWII_IOS = 0x00000007,
TID_HIGH_VWII_SYSTEM_APP = 0x00070002,
TID_HIGH_VWII_SYSTEM = 0x00070008,
TID_HIGH_DLC = 0x0005000C,
TID_HIGH_UPDATE = 0x0005000E,
} TID_HIGH;
#define getTidHighFromTid(tid) ((uint32_t) (tid >> 32))
#define isGame(tid) (getTidHighFromTid(tid) == TID_HIGH_GAME)
#define isDLC(tid) (getTidHighFromTid(tid) == TID_HIGH_DLC)
#define isUpdate(tid) (getTidHighFromTid(tid) == TID_HIGH_UPDATE)
typedef enum {
TITLE_CATEGORY_GAME = 0,
TITLE_CATEGORY_UPDATE = 1,
TITLE_CATEGORY_DLC = 2,
TITLE_CATEGORY_DEMO = 3,
TITLE_CATEGORY_ALL = 4,
TITLE_CATEGORY_DISC = 5,
} TITLE_CATEGORY;
typedef enum {
TITLE_KEY_mypass = 0,
TITLE_KEY_nintendo = 1,
TITLE_KEY_test = 2,
TITLE_KEY_1234567890 = 3,
TITLE_KEY_Lucy131211 = 4,
TITLE_KEY_fbf10 = 5,
TITLE_KEY_5678 = 6,
TITLE_KEY_1234 = 7,
TITLE_KEY_ = 8,
TITLE_KEY_MAGIC = 9,
} TITLE_KEY;
typedef struct
{
const char *name;
const uint64_t tid;
const MCPRegion region;
const TITLE_KEY key;
} TitleEntry;
const TitleEntry *getTitleEntries(TITLE_CATEGORY cat);
size_t getTitleEntriesSize(TITLE_CATEGORY cat);
#ifdef __cplusplus
}
#endif

12
hash.go
View file

@ -13,12 +13,12 @@ import (
var commonKey = []byte{0xD7, 0xB0, 0x04, 0x02, 0x65, 0x9B, 0xA2, 0xAB, 0xD2, 0xCB, 0x0D, 0xB2, 0x7F, 0xA2, 0xB6, 0x56}
func checkContentHashes(path string, content contentInfo, cipherHashTree cipher.Block) error {
h3Data, err := os.ReadFile(filepath.Join(path, fmt.Sprintf("%s.h3", content.ID)))
func checkContentHashes(path string, content Content, cipherHashTree cipher.Block) error {
h3Data, err := os.ReadFile(filepath.Join(path, fmt.Sprintf("%08X.h3", content.ID)))
if err != nil {
return fmt.Errorf("failed to read H3 hash tree file: %w", err)
}
encryptedFile, err := os.Open(filepath.Join(path, fmt.Sprintf("%s.app", content.ID)))
encryptedFile, err := os.Open(filepath.Join(path, fmt.Sprintf("%08X.app", content.ID)))
if err != nil {
return fmt.Errorf("failed to open encrypted file: %w", err)
}
@ -81,9 +81,3 @@ func checkContentHashes(path string, content contentInfo, cipherHashTree cipher.
}
return nil
}
type contentInfo struct {
ID string
Hash []byte
Size int64
}

View file

@ -1,11 +1,19 @@
package wiiudownloader
import (
"cmp"
"os"
"path/filepath"
"strings"
)
func min[T cmp.Ordered](a, b T) T {
if a < b {
return a
}
return b
}
func isThisDecryptedFile(path string) bool {
return strings.Contains(path, "code") || strings.Contains(path, "content") || strings.Contains(path, "meta")
}