From d3949b90bdc8f475d731777904c23b403223accf Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Sun, 19 May 2019 05:10:26 +0300 Subject: [PATCH] CTR IV implemented for NCA3+key+crypto-type=3 --- .../Controllers/NCA/NCAController.java | 12 +- src/main/java/konogonka/LoperConverter.java | 8 ++ .../konogonka/Tools/NCA/NCAContentPFS0.java | 125 +++++++++++------- .../java/konogonka/Tools/NCA/NCAProvider.java | 1 + .../NCASectionTableBlock/NCASectionBlock.java | 14 +- .../NCASectionTableBlock/SuperBlockPFS0.java | 10 -- src/main/java/konogonka/ctraes/AesCtr.java | 34 +++++ .../resources/FXML/NCA/NCASectionContent.fxml | 50 ++++--- src/main/resources/FXML/NCA/NCATab.fxml | 4 +- 9 files changed, 167 insertions(+), 91 deletions(-) create mode 100644 src/main/java/konogonka/ctraes/AesCtr.java diff --git a/src/main/java/konogonka/Controllers/NCA/NCAController.java b/src/main/java/konogonka/Controllers/NCA/NCAController.java index 36ebc8b..fc32cc2 100644 --- a/src/main/java/konogonka/Controllers/NCA/NCAController.java +++ b/src/main/java/konogonka/Controllers/NCA/NCAController.java @@ -178,10 +178,14 @@ public class NCAController implements TabController { NCASectionHeaderFourthController.populateTab(ncaProvider.getSectionBlock3()); // Section content blocks // TODO: FIX: This code executes getNCAContentPFS0() method twice - NCASectionContentFirstController.populateFields(ncaProvider.getNCAContentPFS0(0).getPfs0(), selectedFile, ncaProvider.getNCAContentPFS0(0).getSHA256hashes()); - NCASectionContentSecondController.populateFields(ncaProvider.getNCAContentPFS0(1).getPfs0(), selectedFile, ncaProvider.getNCAContentPFS0(1).getSHA256hashes()); - NCASectionContentThirdController.populateFields(ncaProvider.getNCAContentPFS0(2).getPfs0(), selectedFile, ncaProvider.getNCAContentPFS0(2).getSHA256hashes()); - NCASectionContentFourthController.populateFields(ncaProvider.getNCAContentPFS0(3).getPfs0(), selectedFile, ncaProvider.getNCAContentPFS0(3).getSHA256hashes()); + NCAContentPFS0 ncaContentPFS0 = ncaProvider.getNCAContentPFS0(0); + NCASectionContentFirstController.populateFields(ncaContentPFS0.getPfs0(), selectedFile, ncaContentPFS0.getSHA256hashes()); + ncaContentPFS0 = ncaProvider.getNCAContentPFS0(1); + NCASectionContentSecondController.populateFields(ncaContentPFS0.getPfs0(), selectedFile, ncaContentPFS0.getSHA256hashes()); + ncaContentPFS0 = ncaProvider.getNCAContentPFS0(2); + NCASectionContentThirdController.populateFields(ncaContentPFS0.getPfs0(), selectedFile, ncaContentPFS0.getSHA256hashes()); + ncaContentPFS0 = ncaProvider.getNCAContentPFS0(3); + NCASectionContentFourthController.populateFields(ncaContentPFS0.getPfs0(), selectedFile, ncaContentPFS0.getSHA256hashes()); } } } diff --git a/src/main/java/konogonka/LoperConverter.java b/src/main/java/konogonka/LoperConverter.java index f1c2c57..6ee0448 100644 --- a/src/main/java/konogonka/LoperConverter.java +++ b/src/main/java/konogonka/LoperConverter.java @@ -19,4 +19,12 @@ public class LoperConverter { sb.append(String.format("%02x", b)); return sb.toString(); } + public static byte[] flip(byte[] bytes){ + int size = bytes.length; + byte[] ret = new byte[size]; + for (int i = 0; i < size; i++){ + ret[size-i-1] = bytes[i]; + } + return ret; + } } diff --git a/src/main/java/konogonka/Tools/NCA/NCAContentPFS0.java b/src/main/java/konogonka/Tools/NCA/NCAContentPFS0.java index 3315042..b4d9e01 100644 --- a/src/main/java/konogonka/Tools/NCA/NCAContentPFS0.java +++ b/src/main/java/konogonka/Tools/NCA/NCAContentPFS0.java @@ -1,22 +1,20 @@ package konogonka.Tools.NCA; +import konogonka.LoperConverter; import konogonka.RainbowHexDump; import konogonka.Tools.NCA.NCASectionTableBlock.NCASectionBlock; import konogonka.Tools.PFS0.PFS0Provider; -import org.bouncycastle.jce.provider.BouncyCastleProvider; +import konogonka.ctraes.AesCtr; -import javax.crypto.Cipher; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; import java.io.File; import java.io.RandomAccessFile; -import java.security.Security; import java.util.LinkedList; public class NCAContentPFS0 { private LinkedList SHA256hashes; private PFS0Provider pfs0; + // TODO: if decryptedKey is empty, thorow exception ?? public NCAContentPFS0(File file, long offsetPosition, NCASectionBlock ncaSectionBlock, NCAHeaderTableEntry ncaHeaderTableEntry, byte[] decryptedKey){ SHA256hashes = new LinkedList<>(); try { @@ -47,14 +45,80 @@ public class NCAContentPFS0 { raf.close(); } - // If encrypted (regular) - else if (ncaSectionBlock.getCryptoType() == 0x3){ - long thisMediaLocation = offsetPosition + (ncaHeaderTableEntry.getMediaStartOffset() * 0x200); // todo: use this location for CTR - long hashTableLocation = thisMediaLocation + ncaSectionBlock.getSuperBlockPFS0().getHashTableOffset(); - long pfs0Location = thisMediaLocation + ncaSectionBlock.getSuperBlockPFS0().getPfs0offset(); + // If encrypted (regular) todo: check keys provided + else if (ncaSectionBlock.getCryptoType() == 0x3){ // d0c1... + //-------------------------------------------------------------------------------------------------- + System.out.println("Media start location: " + ncaHeaderTableEntry.getMediaStartOffset()); + System.out.println("Media end location: " + ncaHeaderTableEntry.getMediaEndOffset()); + System.out.println("Media size = sha h.tbl.size: " + (ncaHeaderTableEntry.getMediaEndOffset()-ncaHeaderTableEntry.getMediaStartOffset())); + System.out.println("Media act. location: " + (offsetPosition + (ncaHeaderTableEntry.getMediaStartOffset() * 0x200))); + System.out.println("SHA256 hash tbl size: " + ncaSectionBlock.getSuperBlockPFS0().getHashTableSize()); + System.out.println("SHA256 records: " + (ncaSectionBlock.getSuperBlockPFS0().getHashTableSize() / 0x20)); + System.out.println("KEY: " + LoperConverter.byteArrToHexString(decryptedKey)); + System.out.println(); + //-------------------------------------------------------------------------------------------------- + long thisMediaLocation = offsetPosition + (ncaHeaderTableEntry.getMediaStartOffset() * 0x200); // According to real file + long hashTableLocation = thisMediaLocation + ncaSectionBlock.getSuperBlockPFS0().getHashTableOffset(); // According to real file - raf.seek(hashTableLocation); + raf.seek(thisMediaLocation); + try { + // IV for CTR == 32 bytes + byte[] IVarray = new byte[0x10]; + // Populate first 8 bytes taken from Header's section Block CTR + System.arraycopy(LoperConverter.flip(ncaSectionBlock.getSectionCTR()), 0, IVarray,0, 8); + // Populate last 8 bytes calculated. Thanks hactool project! + // TODO: here is too much magic. It MUST be clarified and simplified + long mediaStrtOffReal = ncaHeaderTableEntry.getMediaStartOffset() * 0x200; // NOTE: long actually should be unsigned.. for calculation it's not critical, but for representation it is + mediaStrtOffReal >>= 4; + for (int i = 0; i < 0x8; i++){ + IVarray[0x10-i-1] = (byte)(mediaStrtOffReal & 0xff); // Note: issues could be here + mediaStrtOffReal >>= 8; + } + + AesCtr aesCtr = new AesCtr(decryptedKey, IVarray); + + byte[] encryptedBlock; + byte[] dectyptedBlock; + long mediaBlockSize = ncaHeaderTableEntry.getMediaEndOffset()-ncaHeaderTableEntry.getMediaStartOffset(); + + for (int i = 0; i < mediaBlockSize; i++){ + encryptedBlock = new byte[0x200]; + if (raf.read(encryptedBlock) != -1){ + dectyptedBlock = aesCtr.decrypt(encryptedBlock); + + + + + + } + } + + /* + System.arraycopy(dectyptedBlock, 0, decryptedHeader, i * 0x200, 0x200); + */ + + + //RainbowHexDump.hexDumpUTF8(dectyptedBlock); + + /* + // Calculate hashes count + long sha256recordsNumber = ncaSectionBlock.getSuperBlockPFS0().getHashTableSize() / 0x20; + + long currentHashStart = ncaSectionBlock.getSuperBlockPFS0().getHashTableOffset(); + for (int i = 0; i < sha256recordsNumber; i++){ + currentHashStart += i * 0x20; + encryptedBlock = Arrays.copyOfRange(dectyptedBlock, currentHashStart, currentHashStart + 0x20); //[0x20]; // 32 bytes - size of SHA256 hash + SHA256hashes.add(encryptedBlock); + } + */ + } + catch (Exception e){ + e.printStackTrace(); + } + + + /* byte[] rawData; long sha256recordsNumber = ncaSectionBlock.getSuperBlockPFS0().getHashTableSize() / 0x20; // Collect hashes @@ -69,42 +133,7 @@ public class NCAContentPFS0 { raf.seek(pfs0Location); rawData = new byte[0x20]; // 32 bytes - size of SHA256 hash - - /* - if (raf.read(rawData) != -1) { - System.out.println("Encrypted"); - RainbowHexDump.hexDumpUTF8(rawData); - } - */ - try { - /* - System.out.println("Decrypted?"); - Security.addProvider(new BouncyCastleProvider()); // TODO: DO FUCKING REMEMBER THIS SHIT FOR CTR - IvParameterSpec iv = new IvParameterSpec(new byte[10]); - SecretKeySpec key = new SecretKeySpec(decryptedKey, "AES"); - Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC"); - cipher.init(Cipher.DECRYPT_MODE, key, iv); - byte[] decr = cipher.doFinal(rawData); - RainbowHexDump.hexDumpUTF8(decr); - - */ - } - catch (Exception e){ - e.printStackTrace(); - } - - - - - - - - - - - - - + */ @@ -125,4 +154,4 @@ public class NCAContentPFS0 { public LinkedList getSHA256hashes() { return SHA256hashes; } public PFS0Provider getPfs0() { return pfs0; } -} +} \ No newline at end of file diff --git a/src/main/java/konogonka/Tools/NCA/NCAProvider.java b/src/main/java/konogonka/Tools/NCA/NCAProvider.java index 6e2ceb6..80057d1 100644 --- a/src/main/java/konogonka/Tools/NCA/NCAProvider.java +++ b/src/main/java/konogonka/Tools/NCA/NCAProvider.java @@ -195,6 +195,7 @@ public class NCAProvider { } else { + // TODO } diff --git a/src/main/java/konogonka/Tools/NCA/NCASectionTableBlock/NCASectionBlock.java b/src/main/java/konogonka/Tools/NCA/NCASectionTableBlock/NCASectionBlock.java index 1285860..117343e 100644 --- a/src/main/java/konogonka/Tools/NCA/NCASectionTableBlock/NCASectionBlock.java +++ b/src/main/java/konogonka/Tools/NCA/NCASectionTableBlock/NCASectionBlock.java @@ -1,5 +1,6 @@ package konogonka.Tools.NCA.NCASectionTableBlock; +import java.lang.reflect.Array; import java.nio.charset.StandardCharsets; import java.util.Arrays; @@ -30,8 +31,7 @@ public class NCASectionBlock { private int BKTRs32Section2; private byte[] BKTRunknownSection2; - private byte[] sectionCTRlow; - private byte[] sectionCTRhigh; + private byte[] sectionCTR; private byte[] unknownEndPadding; public NCASectionBlock(byte[] tableBlockBytes) throws Exception{ @@ -65,8 +65,7 @@ public class NCASectionBlock { BKTRs32Section2 = getLEint(BKTRfullHeader, 0x38); BKTRunknownSection2 = Arrays.copyOfRange(BKTRfullHeader, 0x3c, 0x40); - sectionCTRlow = Arrays.copyOfRange(tableBlockBytes, 0x140, 0x144); - sectionCTRhigh = Arrays.copyOfRange(tableBlockBytes, 0x144, 0x148); + sectionCTR = Arrays.copyOfRange(tableBlockBytes, 0x140, 0x148); unknownEndPadding = Arrays.copyOfRange(tableBlockBytes, 0x148, 0x200); } @@ -91,8 +90,11 @@ public class NCASectionBlock { public int getBKTRu32Section2() { return BKTRu32Section2; } public int getBKTRs32Section2() { return BKTRs32Section2; } public byte[] getBKTRunknownSection2() { return BKTRunknownSection2; } - public byte[] getSectionCTRlow() { return sectionCTRlow; } - public byte[] getSectionCTRhigh() { return sectionCTRhigh; } + public byte[] getSectionCTR() { return sectionCTR; } + // Sugar + public byte[] getSectionCTRlow() { return Arrays.copyOfRange(sectionCTR, 0, 0x8); } + public byte[] getSectionCTRhigh() { return Arrays.copyOfRange(sectionCTR, 0x8, 0x10); } + public byte[] getUnknownEndPadding() { return unknownEndPadding; } } diff --git a/src/main/java/konogonka/Tools/NCA/NCASectionTableBlock/SuperBlockPFS0.java b/src/main/java/konogonka/Tools/NCA/NCASectionTableBlock/SuperBlockPFS0.java index 9704eef..9982de8 100644 --- a/src/main/java/konogonka/Tools/NCA/NCASectionTableBlock/SuperBlockPFS0.java +++ b/src/main/java/konogonka/Tools/NCA/NCASectionTableBlock/SuperBlockPFS0.java @@ -24,16 +24,6 @@ public class SuperBlockPFS0 { pfs0offset = getLElong(sbBytes, 0x38); pfs0size = getLElong(sbBytes, 0x40); zeroes = Arrays.copyOfRange(sbBytes, 0x48, 0xf8); - /* - RainbowHexDump.hexDumpUTF8(SHA256hash); - System.out.println(blockSize); - System.out.println(unknownNumberTwo); - System.out.println(hashTableOffset); - System.out.println(hashTableSize); - System.out.println(pfs0offset); - System.out.println(pfs0size); - RainbowHexDump.hexDumpUTF8(zeroes); - */ } public byte[] getSHA256hash() { return SHA256hash; } diff --git a/src/main/java/konogonka/ctraes/AesCtr.java b/src/main/java/konogonka/ctraes/AesCtr.java new file mode 100644 index 0000000..dd58ddb --- /dev/null +++ b/src/main/java/konogonka/ctraes/AesCtr.java @@ -0,0 +1,34 @@ +package konogonka.ctraes; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.security.Security; + +public class AesCtr { + + private static boolean BCinitialized = false; + + private void initBCProvider(){ + Security.addProvider(new BouncyCastleProvider()); + BCinitialized = true; + } + + private Cipher cipher; + + public AesCtr(byte[] IVarray, byte[] keyArray) throws Exception{ + if ( ! BCinitialized) + initBCProvider(); + + IvParameterSpec iv = new IvParameterSpec(IVarray); + SecretKeySpec key = new SecretKeySpec(keyArray, "AES"); + cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC"); + cipher.init(Cipher.DECRYPT_MODE, key, iv); + } + + public byte[] decrypt(byte[] encryptedData) throws Exception{ + return cipher.doFinal(encryptedData); + } +} diff --git a/src/main/resources/FXML/NCA/NCASectionContent.fxml b/src/main/resources/FXML/NCA/NCASectionContent.fxml index 0ad2f62..5ab387c 100644 --- a/src/main/resources/FXML/NCA/NCASectionContent.fxml +++ b/src/main/resources/FXML/NCA/NCASectionContent.fxml @@ -13,27 +13,35 @@ - - - - - - - + + + + + + + + + + + + + + + diff --git a/src/main/resources/FXML/NCA/NCATab.fxml b/src/main/resources/FXML/NCA/NCATab.fxml index 99779e3..a9989fe 100644 --- a/src/main/resources/FXML/NCA/NCATab.fxml +++ b/src/main/resources/FXML/NCA/NCATab.fxml @@ -696,12 +696,12 @@ - + - +