CTR IV implemented for NCA3+key+crypto-type=3

This commit is contained in:
Dmitry Isaenko 2019-05-19 05:10:26 +03:00
parent baf70df47c
commit d3949b90bd
9 changed files with 167 additions and 91 deletions

View file

@ -178,10 +178,14 @@ public class NCAController implements TabController {
NCASectionHeaderFourthController.populateTab(ncaProvider.getSectionBlock3()); NCASectionHeaderFourthController.populateTab(ncaProvider.getSectionBlock3());
// Section content blocks // Section content blocks
// TODO: FIX: This code executes getNCAContentPFS0() method twice // TODO: FIX: This code executes getNCAContentPFS0() method twice
NCASectionContentFirstController.populateFields(ncaProvider.getNCAContentPFS0(0).getPfs0(), selectedFile, ncaProvider.getNCAContentPFS0(0).getSHA256hashes()); NCAContentPFS0 ncaContentPFS0 = ncaProvider.getNCAContentPFS0(0);
NCASectionContentSecondController.populateFields(ncaProvider.getNCAContentPFS0(1).getPfs0(), selectedFile, ncaProvider.getNCAContentPFS0(1).getSHA256hashes()); NCASectionContentFirstController.populateFields(ncaContentPFS0.getPfs0(), selectedFile, ncaContentPFS0.getSHA256hashes());
NCASectionContentThirdController.populateFields(ncaProvider.getNCAContentPFS0(2).getPfs0(), selectedFile, ncaProvider.getNCAContentPFS0(2).getSHA256hashes()); ncaContentPFS0 = ncaProvider.getNCAContentPFS0(1);
NCASectionContentFourthController.populateFields(ncaProvider.getNCAContentPFS0(3).getPfs0(), selectedFile, ncaProvider.getNCAContentPFS0(3).getSHA256hashes()); 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());
} }
} }
} }

View file

@ -19,4 +19,12 @@ public class LoperConverter {
sb.append(String.format("%02x", b)); sb.append(String.format("%02x", b));
return sb.toString(); 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;
}
} }

View file

@ -1,22 +1,20 @@
package konogonka.Tools.NCA; package konogonka.Tools.NCA;
import konogonka.LoperConverter;
import konogonka.RainbowHexDump; import konogonka.RainbowHexDump;
import konogonka.Tools.NCA.NCASectionTableBlock.NCASectionBlock; import konogonka.Tools.NCA.NCASectionTableBlock.NCASectionBlock;
import konogonka.Tools.PFS0.PFS0Provider; 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.File;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.security.Security;
import java.util.LinkedList; import java.util.LinkedList;
public class NCAContentPFS0 { public class NCAContentPFS0 {
private LinkedList<byte[]> SHA256hashes; private LinkedList<byte[]> SHA256hashes;
private PFS0Provider pfs0; private PFS0Provider pfs0;
// TODO: if decryptedKey is empty, thorow exception ??
public NCAContentPFS0(File file, long offsetPosition, NCASectionBlock ncaSectionBlock, NCAHeaderTableEntry ncaHeaderTableEntry, byte[] decryptedKey){ public NCAContentPFS0(File file, long offsetPosition, NCASectionBlock ncaSectionBlock, NCAHeaderTableEntry ncaHeaderTableEntry, byte[] decryptedKey){
SHA256hashes = new LinkedList<>(); SHA256hashes = new LinkedList<>();
try { try {
@ -47,14 +45,80 @@ public class NCAContentPFS0 {
raf.close(); raf.close();
} }
// If encrypted (regular) // If encrypted (regular) todo: check keys provided
else if (ncaSectionBlock.getCryptoType() == 0x3){ else if (ncaSectionBlock.getCryptoType() == 0x3){ // d0c1...
long thisMediaLocation = offsetPosition + (ncaHeaderTableEntry.getMediaStartOffset() * 0x200); // todo: use this location for CTR //--------------------------------------------------------------------------------------------------
long hashTableLocation = thisMediaLocation + ncaSectionBlock.getSuperBlockPFS0().getHashTableOffset(); System.out.println("Media start location: " + ncaHeaderTableEntry.getMediaStartOffset());
long pfs0Location = thisMediaLocation + ncaSectionBlock.getSuperBlockPFS0().getPfs0offset(); 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; byte[] rawData;
long sha256recordsNumber = ncaSectionBlock.getSuperBlockPFS0().getHashTableSize() / 0x20; long sha256recordsNumber = ncaSectionBlock.getSuperBlockPFS0().getHashTableSize() / 0x20;
// Collect hashes // Collect hashes
@ -69,42 +133,7 @@ public class NCAContentPFS0 {
raf.seek(pfs0Location); raf.seek(pfs0Location);
rawData = new byte[0x20]; // 32 bytes - size of SHA256 hash 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();
}

View file

@ -195,6 +195,7 @@ public class NCAProvider {
} }
else { else {
// TODO
} }

View file

@ -1,5 +1,6 @@
package konogonka.Tools.NCA.NCASectionTableBlock; package konogonka.Tools.NCA.NCASectionTableBlock;
import java.lang.reflect.Array;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
@ -30,8 +31,7 @@ public class NCASectionBlock {
private int BKTRs32Section2; private int BKTRs32Section2;
private byte[] BKTRunknownSection2; private byte[] BKTRunknownSection2;
private byte[] sectionCTRlow; private byte[] sectionCTR;
private byte[] sectionCTRhigh;
private byte[] unknownEndPadding; private byte[] unknownEndPadding;
public NCASectionBlock(byte[] tableBlockBytes) throws Exception{ public NCASectionBlock(byte[] tableBlockBytes) throws Exception{
@ -65,8 +65,7 @@ public class NCASectionBlock {
BKTRs32Section2 = getLEint(BKTRfullHeader, 0x38); BKTRs32Section2 = getLEint(BKTRfullHeader, 0x38);
BKTRunknownSection2 = Arrays.copyOfRange(BKTRfullHeader, 0x3c, 0x40); BKTRunknownSection2 = Arrays.copyOfRange(BKTRfullHeader, 0x3c, 0x40);
sectionCTRlow = Arrays.copyOfRange(tableBlockBytes, 0x140, 0x144); sectionCTR = Arrays.copyOfRange(tableBlockBytes, 0x140, 0x148);
sectionCTRhigh = Arrays.copyOfRange(tableBlockBytes, 0x144, 0x148);
unknownEndPadding = Arrays.copyOfRange(tableBlockBytes, 0x148, 0x200); unknownEndPadding = Arrays.copyOfRange(tableBlockBytes, 0x148, 0x200);
} }
@ -91,8 +90,11 @@ public class NCASectionBlock {
public int getBKTRu32Section2() { return BKTRu32Section2; } public int getBKTRu32Section2() { return BKTRu32Section2; }
public int getBKTRs32Section2() { return BKTRs32Section2; } public int getBKTRs32Section2() { return BKTRs32Section2; }
public byte[] getBKTRunknownSection2() { return BKTRunknownSection2; } public byte[] getBKTRunknownSection2() { return BKTRunknownSection2; }
public byte[] getSectionCTRlow() { return sectionCTRlow; } public byte[] getSectionCTR() { return sectionCTR; }
public byte[] getSectionCTRhigh() { return sectionCTRhigh; } // Sugar
public byte[] getSectionCTRlow() { return Arrays.copyOfRange(sectionCTR, 0, 0x8); }
public byte[] getSectionCTRhigh() { return Arrays.copyOfRange(sectionCTR, 0x8, 0x10); }
public byte[] getUnknownEndPadding() { return unknownEndPadding; } public byte[] getUnknownEndPadding() { return unknownEndPadding; }
} }

View file

@ -24,16 +24,6 @@ public class SuperBlockPFS0 {
pfs0offset = getLElong(sbBytes, 0x38); pfs0offset = getLElong(sbBytes, 0x38);
pfs0size = getLElong(sbBytes, 0x40); pfs0size = getLElong(sbBytes, 0x40);
zeroes = Arrays.copyOfRange(sbBytes, 0x48, 0xf8); 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; } public byte[] getSHA256hash() { return SHA256hash; }

View file

@ -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);
}
}

View file

@ -13,6 +13,10 @@
<TitledPane text="PFS0"> <TitledPane text="PFS0">
<content> <content>
<VBox spacing="5.0"> <VBox spacing="5.0">
<TitledPane expanded="false" text="SHA256 hashes">
<content>
<VBox>
<children>
<HBox VBox.vgrow="ALWAYS"> <HBox VBox.vgrow="ALWAYS">
<children> <children>
<Label text=" #"> <Label text=" #">
@ -33,7 +37,11 @@
</Label> </Label>
</children> </children>
</HBox> </HBox>
<VBox fx:id="sha256pane" VBox.vgrow="ALWAYS" /> <VBox fx:id="sha256pane" />
</children>
</VBox>
</content>
</TitledPane>
<Separator prefWidth="200.0" /> <Separator prefWidth="200.0" />
<fx:include fx:id="SectionPFS0" source="../NSP/NSPTab.fxml" /> <fx:include fx:id="SectionPFS0" source="../NSP/NSPTab.fxml" />
</VBox> </VBox>

View file

@ -696,12 +696,12 @@
<fx:include fx:id="NCASectionContentSecond" source="../NCA/NCASectionContent.fxml" /> <fx:include fx:id="NCASectionContentSecond" source="../NCA/NCASectionContent.fxml" />
</content> </content>
</TitledPane> </TitledPane>
<TitledPane text="Section 2"> <TitledPane expanded="false" text="Section 2">
<content> <content>
<fx:include fx:id="NCASectionContentThird" source="../NCA/NCASectionContent.fxml" /> <fx:include fx:id="NCASectionContentThird" source="../NCA/NCASectionContent.fxml" />
</content> </content>
</TitledPane> </TitledPane>
<TitledPane text="Section 3"> <TitledPane expanded="false" text="Section 3">
<content> <content>
<fx:include fx:id="NCASectionContentFourth" source="../NCA/NCASectionContent.fxml" /> <fx:include fx:id="NCASectionContentFourth" source="../NCA/NCASectionContent.fxml" />
</content> </content>