Hacking Finding the 3DS Common Key

GPUs decrypt crazy fast... If someone created a folding at home type app that could use the GPU, CPU, Cell (hacked playstation 3s) have it go back to a network server.

We could easy get enough computer power to crazy that sucker in no time.
lol

There are 1.15 X 10^77 possible combinations in a 256 bit key.

Assuming a computer could brute force 1 billion keys per second (fat chance) it would take 3.67 X10^60 years to crack on average.

That's 3 followed by sixty zeros years.

To add a level of complexity to the crack, you realize you need to factor prime numbers to that magnitude also, so why not add a few (10^60 or whatever, i'm not about to do the math cuz i'm lazy) years to the mix there

It is computationally infeasible to crack the key which is why encryption works

That makes no sense, if there are 1.15 X 10^77 possibilities in a key then there are that many, there are no more no less, no matter what algorithm they used to develop it. Anyways, you guys are looking at it like it has to be the last key tried, It could be the 42nd key tried and take all of a second, or hell, it could be the first key. You never know.

Just so you know it's not as simple as checking for the key and dividing or whatever you think they do. You have to have the key, its relative prime factors and its inverse to properly decrypt
 
Just for fun

Code:
//snippy long ass source code :P
http://www.mediafire...hbi3ty1f4aed664
Here is the compiled binary and source files for Ichifly's AES decryptor in case anybody's feeling lucky. :P

Oh yeah, I got this prompt Ichifly:
"NUS Content has changed. Notify author, although program should still work..."
 
Just for fun

Code:
//snippy long ass source code :P
http://www.mediafire...hbi3ty1f4aed664
Here is the compiled binary and source files for Ichifly's AES decryptor in case anybody's feeling lucky. :P

Oh yeah, I got this prompt Ichifly:
"NUS Content has changed. Notify author, although program should still work..."

sorry I posted the wrong Version
Code:
using System;
using System.Security.Cryptography;
using System.IO;
using System.Net;
using System.Threading;
using System.Timers;

namespace AES_Decrypter
{
class Program
{
static RijndaelManaged rijndaelCipher;
//static SHA1 sha = new SHA1CryptoServiceProvider();
static SHA256 sha = new SHA256Managed();

static Random rnd = new Random();
static RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();

static byte[] buffer;
static int num_tried;
static long total_num;
static bool keepgoing;

static void Main(string[] args)
{
Console.WriteLine("[]-[]-[]-[]-[]-[]-[]-[] Basic 3DS Bruteforce Application []-[]-[]-[]-[]-[]-[]-[]");

// Create all the constants/variables...
const string DSiNUSURL = "http://nus.cdn.t.shop.nintendowifi.net/ccs/download/";
byte[] contsha1 = new byte[0x20] { 0x10, 0xb2, 0x8d, 0x8e, 0xcb, 0xcc, 0x73, 0xbc, 0xef, 0x40, 0xb0, 0x24, 0x06, 0x1f, 0x4f, 0xd6, 0xb8, 0x14, 0xdf, 0x08, 0x10, 0xb2, 0x8d, 0x8e, 0xcb, 0xcc, 0x73, 0xbc, 0xef, 0x40, 0xb0, 0x24};
byte[] testkey = new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte[] titleIV = new byte[16] { 0x00, 0x03, 0x00, 0x0f, 0x48, 0x4e, 0x4c, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte[] contentIV = new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte[] enccontentsha = new byte[0x20] { 0xB2, 0x56, 0x91, 0x55, 0xFC, 0xA3, 0xBE, 0x49, 0xAD, 0x88, 0xE1, 0xBB, 0xA8, 0x99, 0x2C, 0x6E, 0xB3, 0x60, 0xA5, 0xC2, 0x97, 0xC6, 0xA9, 0xE4, 0x8B, 0x6A, 0x94, 0xC9, 0xA6, 0x70, 0x79, 0xCB };
byte[] decfirstbytes = new byte[16] { 0x17, 0xFC, 0xB6, 0xF1, 0x78, 0x4F, 0x8D, 0x1E, 0x93, 0xE2, 0x47, 0x4F, 0x9C, 0x5F, 0x0E, 0xC5 };

Console.WriteLine("Downloading files to decrypt...\n");
WebClient generalWC = new WebClient();
byte[] data = null;
try
{
FileStream localtitle = new FileStream(@"/00000000", FileMode.Open);
data = ReadFully(localtitle);
localtitle.Close();

}
catch
{
try
{
data = generalWC.DownloadData(DSiNUSURL + "0004800F484E4C41/00000000");
}
catch
{ }
}
finally
{
if (data == null)
{
// Fail
Console.WriteLine("Error: NUS 00000003 not found online or locally!");
Console.ReadLine();
Environment.Exit(0);
}
}
Console.WriteLine(" - Received: 00000003");
byte[] titlekeytemp = null;
try
{
FileStream localtitle = new FileStream(@"\cetk", FileMode.Open);
titlekeytemp = ReadFully(localtitle);
localtitle.Close();
}
catch
{
try
{
titlekeytemp = generalWC.DownloadData(DSiNUSURL + "0004800F484E4C41/cetk");
}
catch
{ }
}
finally
{
if (titlekeytemp == null)
{
// Fail
Console.WriteLine("Error: NUS ticket not found online or locally!");
Console.ReadLine();
Environment.Exit(0);
}
}
Console.WriteLine(" - Received: Ticket (cetk)");
byte[] tmd = null;
try
{
FileStream localtitle = new FileStream(@"\tmd", FileMode.Open);
tmd = ReadFully(localtitle);
localtitle.Close();
}
catch
{
try
{
tmd = generalWC.DownloadData(DSiNUSURL + "0004800F484E4C41/tmd");
}
catch
{ }
}
finally
{
if (tmd == null)
{
// Fail
Console.WriteLine("Error: NUS tmd not found online or locally!");
Console.ReadLine();
Environment.Exit(0);
}
}
Console.WriteLine(" - Received: Title metadata (TMD)\n");

// Load latest SHA-1 from TMD
for (int i = 0; i < 0x20; i++)
{
contsha1[i] = tmd[0xB14 + i];
}
Console.WriteLine("Detected SHA-1: ");
DisplayBytes(contsha1);

// Optimize. Do this to reduce conversion later...
string decstring = Convert.ToBase64String(decfirstbytes);

contentIV[15] = tmd[0xB09]; //ichfly only use ID 0
Console.WriteLine("\n\nUsing IV: ");
DisplayBytes(contentIV);


if (Convert.ToBase64String(sha.ComputeHash(data)) != /*Convert.ToBase64String(enccontentsha)*/"ElKL53rGbVr4gczolGRpvcon7pBRQxca2Try4fJx3g4=")
{
Console.WriteLine("NUS Content has changed. Notify author, although program should still work...");
Console.ReadLine();
//Environment.Exit(0);
}

/*Create all the constants...
const string DSiNUSURL = "http://nus.cdn.shop.wii.com/ccs/download/";
byte[] contsha1 = new byte[20] { 0x10, 0x14, 0x79, 0x94, 0xc3, 0xc5, 0x98, 0x10, 0x4f, 0x1d, 0xd8, 0xea, 0xea, 0xf6, 0xa1, 0xea, 0xdc, 0x85, 0xee, 0x44 };
byte[] testkey = new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte[] finikey = new byte[16] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
byte[] titleIV = new byte[16] { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte[] contentIV = new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

Console.WriteLine("Downloading files to decrypt...\n");
WebClient generalWC = new WebClient();
byte[] data = generalWC.DownloadData(DSiNUSURL + "0000000100000010/00000002");
byte[] titlekeytemp = generalWC.DownloadData(DSiNUSURL + "0000000100000010/cetk");*/

// Get the Title Key from cetk...
byte[] titlekeyenc = new byte[16];
for (int i = 0; i < 0x10; i++)
{
titlekeyenc[i] = titlekeytemp[0x1BF + i];
}
Console.WriteLine("\n\nEncrypted Title Key of 03 Content...");
DisplayBytes(titlekeyenc);

// Take in any hex codes as parameters...
for (int i = 0; i < args.Length; i++)
{
testkey[i] = byte.Parse(args[i], System.Globalization.NumberStyles.HexNumber);
}

if (args.Length >= 1)
{
Console.WriteLine("\n\nCurrent starting point...");
DisplayBytes(testkey);
}

// Set 'mode' based on input...
Console.WriteLine("\n\nMode:");
Console.WriteLine("  [1] Standard");
Console.WriteLine("  [2] 'Secure' Random");
Console.WriteLine("  [3] '1' Bit Mode");
Console.WriteLine("\nObsolete Modes:");
Console.WriteLine("  [4] Standard Random");
Console.WriteLine("  [D] Debug Mode");
Console.WriteLine("\nSelect a mode: ");
int mode = 0;
System.ConsoleKeyInfo cks = Console.ReadKey(true);
if (cks.KeyChar == '1')
mode = 1;
else if (cks.KeyChar == '2')
mode = 2;
else if (cks.KeyChar == '3')
mode = 3;
else if (cks.KeyChar == '4')
mode = 4;
else if (cks.KeyChar == 'D')
mode = 9;
else
mode = 3;

// AES stuff
// This is for decryting title key from test common keys
// Cipher1 decrypts the Title Key with random common key
rijndaelCipher = new RijndaelManaged();
rijndaelCipher.Mode = CipherMode.CBC;
rijndaelCipher.Padding = PaddingMode.None;
rijndaelCipher.KeySize = 128;
rijndaelCipher.BlockSize = 128;
rijndaelCipher.IV = titleIV;

// Cipher2 decrypts the contents using the new TitleKey
RijndaelManaged finaldecrypter;
finaldecrypter = new RijndaelManaged();
finaldecrypter.Mode = CipherMode.CBC;
finaldecrypter.Padding = PaddingMode.None;
finaldecrypter.KeySize = 128;
finaldecrypter.BlockSize = 128;
finaldecrypter.IV = contentIV;

// Fun facts on how long it took...
long timeinms = DateTime.UtcNow.Ticks;
long timeinft = 0;

num_tried = 0;
total_num = 0;

// Optimization? lol
buffer = new byte[6992];

// Every 1 second, show info...
System.Timers.Timer t = new System.Timers.Timer(1000);
t.Elapsed += new ElapsedEventHandler(TimerTicked);
t.Start();

// wow
keepgoing = true;

// Small part of decrypted file...
byte[] encsmall = new byte[16];
for (int i = 0; i < 16; i++)
{
encsmall[i] = data[i];
}

while (keepgoing)
{
// Set the Title Key decrypter to the random test key...
rijndaelCipher.Key = testkey;

// The new key is the decrypted title key by test ckey...
finaldecrypter.Key = Decrypt(titlekeyenc, rijndaelCipher);

// If the miraculous SHA-1 should match...
if (Convert.ToBase64String(sha.ComputeHash(Decrypt(data, finaldecrypter))) == Convert.ToBase64String(contsha1))
{
// Common Key is written to console...
Console.WriteLine("Common Key: ");
DisplayBytes(testkey);
Console.WriteLine("Title Key: ");
DisplayBytes(Decrypt(titlekeyenc, rijndaelCipher));
WriteFoundKey(testkey);
Console.WriteLine("\nKey written to /dsikey.bin. DO NOT CLOSE THIS UNTIL YOU CAN VERIFY FILE INTEGRITY.");
Console.WriteLine("\n");
Console.WriteLine("Verifying entire file...");
if (Convert.ToBase64String(sha.ComputeHash(Decrypt(data, finaldecrypter))) == Convert.ToBase64String(contsha1))
Console.WriteLine("SHA-1 test matches :D");
else
Console.WriteLine("SHA-1 failed, still SOMETHING happened :|");
num_tried = 0;
keepgoing = false;
}

// Depending on mode, increment or randomize the test key...
if (mode == 1)
testkey = incrementAtIndex(testkey, 15);
else if (mode == 2)
testkey = SecureRandomize();
else if (mode == 3)
{
testkey = SecureRandomize();
testkey[0] = SetBit(testkey[0], 7, true);
}
else if (mode == 9)
{
Console.WriteLine("\nCurrent COMMON KEY: ");
DisplayBytes(testkey);
Console.WriteLine("\nCurrent TITLE KEY: ");
DisplayBytes(finaldecrypter.Key);
Console.WriteLine("\nThis SHA-1: ");
DisplayBytes(sha.ComputeHash(Decrypt(data, finaldecrypter)));
Console.WriteLine("\nOfficial SHA-1: ");
DisplayBytes(contsha1);
Console.WriteLine("\nEncrypted Chunk: ");
DisplayBytes(encsmall);
Console.WriteLine("\nDecrypted Chunk: ");
DisplayBytes(Decrypt(encsmall, finaldecrypter));
//WriteFoundKey(testkey);
Console.ReadLine();


testkey = SecureRandomize();
testkey[0] = SetBit(testkey[0], 7, true);
}
else if (mode == 4)
testkey = PlainRandomize();

if (mode != 9)
{
num_tried += 1;
total_num += 1;
}
}

// Somehow it worked, so get the new time now...
timeinft = DateTime.UtcNow.Ticks;

// ... and write Done with the total time elapsed...
t.Dispose();
Console.WriteLine("\nDone - " + (timeinft - timeinms) + " Operation");
Console.ReadLine();
Console.ReadLine();
Console.ReadLine();
Console.ReadLine();
}

public static byte[] Decrypt(byte[] encryptedData, RijndaelManaged decrypter)
{
//ICryptoTransform transform = rijndaelCipher.CreateDecryptor();
ICryptoTransform transform = decrypter.CreateDecryptor();
using (MemoryStream ms = new MemoryStream(encryptedData))
{
using (CryptoStream cs = new CryptoStream(ms, transform, CryptoStreamMode.Read))
{
return ReadFully(cs);
}
}
}

public static byte[] ReadFully(Stream stream)
{
buffer = new byte[6992];
using (MemoryStream ms = new MemoryStream())
{
while (true)
{
int read = stream.Read(buffer, 0, buffer.Length);
if (read <= 0)
return ms.ToArray();
ms.Write(buffer, 0, read);
}
}
}

static void DisplayBytes(byte[] bytes)
{
for (int i = 0; i < bytes.Length; ++i)
{
Console.Write(bytes[i].ToString("X2") + " ");
//Console.Write(bytes[i].ToString("x4") + " ");
//if (i > 0 && i % 16 == 0) Console.Write("\n");
}
//Console.WriteLine("");
}

static public byte[] incrementAtIndex(byte[] array, int index)
{
if (array[index] == byte.MaxValue)
{
array[index] = 0;
if (index > 0)
incrementAtIndex(array, index - 1);
}
else
{
array[index]++;
}

return array;
}

static public byte[] PlainRandomize()
{
//Create random byte array
byte[] randomArray = new byte[16];

// Use 'secure' / regular randomization.
rnd.NextBytes(randomArray);

return randomArray;
}

static public byte[] SecureRandomize()
{
//Create random byte array
byte[] randomArray = new byte[16];

// Use 'secure' / regular randomization.
rng.GetNonZeroBytes(randomArray);

return randomArray;
}

static public void WriteFoundKey(byte[] ckey)
{
try
{
//Console.WriteLine("Writing to file...");
//DisplayBytes(ckey);
FileStream ckeystr = new FileStream(@"/dsikey.bin", FileMode.OpenOrCreate);
ckeystr.Write(ckey, 0, 16);
ckeystr.Close();
}
catch
{
//throw;
}

}

private static bool GetBit(byte b, byte position)
{
return ((b & (byte)(1 << position)) != 0);
}

private static byte SetBit(byte b, byte position, bool newBitValue)
{
byte mask = (byte)(1 << position);
if (newBitValue)
{
return (byte)(b | mask);
}
else
{
return (byte)(b & ~mask);
}
}

private static void TimerTicked(object sender, ElapsedEventArgs e)
{
if (num_tried != 0)
{
Console.Clear();
Console.WriteLine(num_tried + " Keys p/s. (Total: " + total_num + ")");

num_tried = 0;
}
}
}
}


This one really should work.
ADD: It is just a port. (from the Basic DSi Bruteforce Application to the 3DS)
ADD2: I can't test it but I think it work chances are 50% that it work. Sorry if there is another bug or if the 3ds handle things different
ADD3: why is everyone calling me ichifly?
 
  • Like
Reactions: 1 person
Geeze can't even go take a crap without snailface ninjaing me again. :P Oh well I'll just mathflood to make up for it.

key.cs(34,20): warning CS0219: The variable `enccontentsha' is assigned but its value is never used
key.cs(131,20): warning CS0219: The variable `decstring' is assigned but its value is never used
Compilation succeeded - 2 warning(s)
(Figured I'd post this anyways.)

This program's single-threaded BTW, so if you have multiple cores you can supposedly run it multiple times at once (as many times as you have real or virtual cores).

This gets about 3000 keys a second on each core of my four-core machine... so out of the ~340,282,366,920,938,463,463,374,607,431,768,211,456 potential keys, my machine can do 12,000 a second.

720,000 a minute.
43,200,000 an hour.
1,036,800,000 a day.
31,104,000,000 a month.
378,432,000,000 a year.

340,282,366,920,938,463,463,374,607,431,768,211,456 total.
000,000,000,000,000,000,000,000,000,378,432,000,000 tested a year.

If I ran this on all four cores for 50 years nonstop, I'd test 18,921,600,000,000 keys.
How large is that number of the total?
5.560 * 10^(-26)

So if my scientific notation and shit is correct, that's...
00.00000000000000000000000556%
of the keys.

And that's assuming the 3DS is using a 128-bit key. It could be using a bigger one.
 
  • Like
Reactions: 1 person
so are people actually attempting this?
would it make a difference if one more person jumped in?
I gotta say by those numbers, it doesn't look like it will lead to the result you'd want
 
Yeah, uh, good luck with that. Someone dig me up when you find the keys, I'll be buried with my triple roll-over lottery winnings that I won 2 months running.
 
Hang on, is this actually a legit program?
It's legit (as stated it's a port of the DSi version), but according to my numbers if I ran it for 50 years it'd still have a 99.99999999999999999999999443% chance of failing.
Yes, but if it's random it means that it won't necessarily take that long. No?
 
Hang on, is this actually a legit program?
It's legit (as stated it's a port of the DSi version), but according to my numbers if I ran it for 50 years it'd still have a 99.99999999999999999999999443% chance of failing.
Yes, but if it's random it means that it won't necessarily take that long. No?
The thing with bruteforcing is that you never really know when you're going to hit the nail on the head. It can take thousands of years, it could take 5 minutes. All the info you really have is probability.
 
It'll tell you, opposed to just displaying some stats like it does normally.

And yes it's random, but like I said if you ran it for 50 years you'd still have a 99.99999999999999999999999443% chance of failure at the end of those 50 years counting all the keys tried.
 

Site & Scene News

Popular threads in this forum