Skip to content

Commit

Permalink
dug out SHA1 code from mbed TLS as sha1_16
Browse files Browse the repository at this point in the history
  • Loading branch information
Jimmy-Z committed Aug 25, 2017
1 parent f1cbbc2 commit 09783bb
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 48 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
PNAME = twlbf
OBJS = $(PNAME).o utils.o dsi.o
OBJS = $(PNAME).o utils.o dsi.o crypto.o
MBEDTLS_OBJS = sha1.o aes.o aesni.o
CFLAGS += -std=c11 -Wall -O2

Expand Down
43 changes: 14 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,22 @@ The Windows version linked with OpenSSL requires libeay32.dll to run,
I suggest you get it from [here](https://indy.fulgan.com/SSL/).

### Usage
- (de/en)crypt a single block(as in AES block, not NAND block):
brute force by providing a known block(as in AES block, not NAND block) to verify against:

`twlbf crypt [Console ID] [EMMC CID] [src] [offset]`

- Console ID, 8 bytes, hex string, so 16 digits long.
- EMMC CID, 16 bytes, hex string.
- src, 16 bytes, hex string.
- offset, 2 bytes, hex string, beware this is block offset.

AES-CTR is symmetric so encrypt and decrypt is the same thing.

example: decrypt a block at 0x1f0(in byte offset):
````
twlbf crypt 08a1522617110121 ab6778e02d034d303046504100001500 \
1ced45c75e810bb6b51a5318e0fc5eee 001f
````
the result should be `000000000000000000000000000055aa`

- brute force by providing a known block to verify against:

`twlbf emmc_cid [Console ID] [EMMC CID] [src] [verify] [offset]`

`twlbf console_id [Console ID] [EMMC CID] [src] [verify] [offset]`

`twlbf console_id_bcd [Console ID] [EMMC CID] [src] [verify] [offset]`
````
twlbf emmc_cid [Console ID] [EMMC CID] [src] [verify] [offset]
twlbf console_id [Console ID] [EMMC CID] [src] [verify] [offset]
twlbf console_id_bcd [Console ID] [EMMC CID] [src] [verify] [offset]
````

- verify, 16 bytes, hex string.
- Console ID, 8 bytes, hex string, so 16 digits long.
- EMMC CID, 16 bytes, hex string.
- src, 16 bytes, hex string.
- verify, 16 bytes, hex string.
- offset, 2 bytes, hex string, beware this is block offset.

usually you should read 16 bytes from EMMC dump at offset 0x1f0 as [src],
use `000000000000000000000000000055aa` as [verify], and `001f` as [offset].
usually you should read 16 bytes from EMMC dump at offset 0x1f0 as [src],
use `000000000000000000000000000055aa` as [verify], and `001f` as [offset].

#### when brute force EMMC CID, the EMMC CID you provided was used as a template.

Expand Down Expand Up @@ -88,8 +74,7 @@ particularly OpenSSL AES can be 5 times faster with that.

EMMC CID brute forcing can be optimized to do AES on large blocks,
for that kind of work OpenSSL's AES-NI implementation is about 1.8x faster than mbed TLS,
overall speed improvement is about 1.4x due to SHA1 costing most of the CPU time.
OpenSSL SHA1 is also a bit faster than mbed TLS.
overall speed improvement is not that impressive due to SHA1 costing most of the CPU time.

Console ID brute forcing unfortunately can't be optimized that way,
we're forced to do lots of 16 byte AES operations,
Expand Down
195 changes: 195 additions & 0 deletions crypto.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
#include <stdint.h>

/* SHA1 code dug out from mbed TLS
* the sha1_16 only supports 16 bytes input and only spit out the first 16 bytes
*/

/* also looked at OpenSSL SHA1
* the C implementation is wrapped in some crazy macro
* they have multiple ASM implementations including AVX(2)/SHAEXT, impressive!
* BTW about SHAEXT, Intel has SHAEXT document dated back to Jul 2013
* but only first implemented in Goldmont(Atom) in Apr 2016
* but strangely not available on Kaby Lake(Oct 2016) and Coffee Lake(Oct 2017)
* for Intel desktop processor we'd wait until Cannonlake(expected H1 2018)
* AMD supported this in Ryzen(Feb 2017)
*/

#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) ] = (unsigned char) ( (n) >> 24 ); \
(b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \
(b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \
(b)[(i) + 3] = (unsigned char) ( (n) ); \
}
#endif

static const uint32_t
h0 = 0x67452301,
h1 = 0xEFCDAB89,
h2 = 0x98BADCFE,
h3 = 0x10325476,
h4 = 0xC3D2E1F0;

void sha1_16(unsigned char out[16], const unsigned char in[16]) {
uint32_t temp, W[16],
A = h0, B = h1, C = h2, D = h3, E = h4;

GET_UINT32_BE(W[0], in, 0);
GET_UINT32_BE(W[1], in, 4);
GET_UINT32_BE(W[2], in, 8);
GET_UINT32_BE(W[3], in, 12);
W[4] = 0x80000000u; W[5] = 0; W[6] = 0; W[7] = 0;
W[8] = 0; W[9] = 0; W[10] = 0; W[11] = 0;
W[12] = 0; W[13] = 0; W[14] = 0; W[15] = 0x80;

// puts(hexdump(W, 64, 1));

#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); \
}

#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

#undef S
#undef R
#undef P

// we only needs 16 bytes
A += h0;
B += h1;
C += h2;
D += h3;

PUT_UINT32_BE(A, out, 0);
PUT_UINT32_BE(B, out, 4);
PUT_UINT32_BE(C, out, 8);
PUT_UINT32_BE(D, out, 12);
}

1 change: 1 addition & 0 deletions crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "common.h"

void sha1(u8 *out, const u8 *in, unsigned len);
void sha1_16(u8 out[16], const u8 in[16]);

void crypto_init();
void aes_128_ecb_set_key(const u8 *key);
Expand Down
18 changes: 9 additions & 9 deletions crypto_mbedtls.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@
#include "mbedtls/aesni.h"
#include "crypto.h"

void sha1(u8 *out, const u8 *in, unsigned len){
mbedtls_sha1(in, len, out);
}

static mbedtls_aes_context ctx;
static mbedtls_aes_context ctx_aes;

int (*p_aes_crypt_ecb)(mbedtls_aes_context*, int, const unsigned char *, unsigned char *) = NULL;

Expand All @@ -24,21 +20,25 @@ void crypto_init(){
puts(", AES-NI not supported");
p_aes_crypt_ecb = mbedtls_aes_crypt_ecb;
}
mbedtls_aes_init(&ctx);
mbedtls_aes_init(&ctx_aes);
}

void sha1(u8 *out, const u8 *in, unsigned len){
mbedtls_sha1(in, len, out);
}

void aes_128_ecb_set_key(const u8 *key){
mbedtls_aes_setkey_enc(&ctx, key, 128);
mbedtls_aes_setkey_enc(&ctx_aes, key, 128);
}

void aes_128_ecb_crypt_1(u8 *out, const u8 *in){
p_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, in, out);
p_aes_crypt_ecb(&ctx_aes, MBEDTLS_AES_ENCRYPT, in, out);
}

void aes_128_ecb_crypt(u8 *out, const u8 *in, unsigned len){
len >>= 4;
for(unsigned i = 0; i < len; ++i){
p_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, in, out);
p_aes_crypt_ecb(&ctx_aes, MBEDTLS_AES_ENCRYPT, in, out);
in += 16;
out += 16;
}
Expand Down
18 changes: 9 additions & 9 deletions dsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ long long time_diff(const struct timeval *t1, const struct timeval *t0){
// http://problemkaputt.de/gbatek.htm
// https://github.com/WinterMute/twltool

const u32 DSi_KEY_Y[4] =
{0x0ab9dc76, 0xbd4dc4d3, 0x202ddd1d, 0xe1a00005};
const u64 DSi_KEY_Y[2] =
{0xbd4dc4d30ab9dc76ull, 0xe1a00005202ddd1dull};

const u32 DSi_KEY_MAGIC[4] =
{0x1a4f3e79, 0x2a680f5f, 0x29590258, 0xfffefb4e};
const u64 DSi_KEY_MAGIC[2] =
{0x2a680f5f1a4f3e79ull, 0xfffefb4e29590258ull};

static inline u64 u64be(const u8 *in){
u64 out;
Expand Down Expand Up @@ -112,8 +112,8 @@ static inline void dsi_make_key(u64 *key, u64 console_id){
u32 key_x[4] = {l, l ^ 0x24ee6906, h ^ 0xe65b601d, h};
// Key = ((Key_X XOR Key_Y) + FFFEFB4E295902582A680F5F1A4F3E79h) ROL 42
// equivalent to F_XY in twltool/f_xy.c
xor_128(key, (u64*)key_x, (u64*)DSi_KEY_Y);
add_128(key, (u64*)DSi_KEY_MAGIC);
xor_128(key, (u64*)key_x, DSi_KEY_Y);
add_128(key, DSi_KEY_MAGIC);
rol42_128(key);
}

Expand All @@ -125,7 +125,7 @@ void dsi_aes_ctr_crypt_block(const u8 *console_id, const u8 *emmc_cid,
printf("AES-CTR KEY: %s\n", hexdump(key, 16, 1));

u8 emmc_cid_sha1[20];
sha1(emmc_cid_sha1, emmc_cid, 16);
sha1_16(emmc_cid_sha1, emmc_cid);
printf("AES-CTR IV: %s\n", hexdump(emmc_cid_sha1, 16, 1));

printf("Source: %s\n", hexdump(src, 16, 1));
Expand Down Expand Up @@ -213,7 +213,7 @@ void dsi_brute_emmc_cid(const u8 *console_id, const u8 *emmc_cid_template,
#endif
for(unsigned j = 0; j < CHUNK_LEN; ++j){
u8 emmc_cid_sha1[20];
sha1(emmc_cid_sha1, emmc_cid, 16);
sha1_16(emmc_cid_sha1, emmc_cid);
add_128_64((u64*)emmc_cid_sha1, offset64);
byte_reverse_16(ctr_chunk + BLOCK_SIZE * j, emmc_cid_sha1);
*(u32*)(emmc_cid + 1) += 1;
Expand Down Expand Up @@ -263,7 +263,7 @@ void dsi_brute_console_id(const u8 *console_id_template, const u8 *emmc_cid,
u64 target_xor_h64 = u64be(target_xor);

u8 emmc_cid_sha1[20];
sha1(emmc_cid_sha1, emmc_cid, 16);
sha1_16(emmc_cid_sha1, emmc_cid);
add_128_64((u64*)emmc_cid_sha1, u16be(offset));
u8 ctr[16];
byte_reverse_16(ctr, emmc_cid_sha1);
Expand Down

0 comments on commit 09783bb

Please sign in to comment.