CTR IV implemented for NCA3+key+crypto-type=3
This commit is contained in:
parent
baf70df47c
commit
d3949b90bd
9 changed files with 167 additions and 91 deletions
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -195,6 +195,7 @@ public class NCAProvider {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
34
src/main/java/konogonka/ctraes/AesCtr.java
Normal file
34
src/main/java/konogonka/ctraes/AesCtr.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue