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());
// 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());
}
}
}

View file

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

View file

@ -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<byte[]> 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<byte[]> getSHA256hashes() { return SHA256hashes; }
public PFS0Provider getPfs0() { return pfs0; }
}
}

View file

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

View file

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

View file

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

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,27 +13,35 @@
<TitledPane text="PFS0">
<content>
<VBox spacing="5.0">
<HBox VBox.vgrow="ALWAYS">
<children>
<Label text=" #">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
<font>
<Font name="System Bold" size="13.0" />
</font>
</Label>
<Label text="SHA256 Hash">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
<font>
<Font name="System Bold" size="13.0" />
</font>
</Label>
</children>
</HBox>
<VBox fx:id="sha256pane" VBox.vgrow="ALWAYS" />
<TitledPane expanded="false" text="SHA256 hashes">
<content>
<VBox>
<children>
<HBox VBox.vgrow="ALWAYS">
<children>
<Label text=" #">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
<font>
<Font name="System Bold" size="13.0" />
</font>
</Label>
<Label text="SHA256 Hash">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
<font>
<Font name="System Bold" size="13.0" />
</font>
</Label>
</children>
</HBox>
<VBox fx:id="sha256pane" />
</children>
</VBox>
</content>
</TitledPane>
<Separator prefWidth="200.0" />
<fx:include fx:id="SectionPFS0" source="../NSP/NSPTab.fxml" />
</VBox>

View file

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