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());
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -195,6 +195,7 @@ public class NCAProvider {
|
|||
}
|
||||
else {
|
||||
|
||||
// TODO
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
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">
|
||||
<content>
|
||||
<VBox spacing="5.0">
|
||||
<TitledPane expanded="false" text="SHA256 hashes">
|
||||
<content>
|
||||
<VBox>
|
||||
<children>
|
||||
<HBox VBox.vgrow="ALWAYS">
|
||||
<children>
|
||||
<Label text=" #">
|
||||
|
@ -33,7 +37,11 @@
|
|||
</Label>
|
||||
</children>
|
||||
</HBox>
|
||||
<VBox fx:id="sha256pane" VBox.vgrow="ALWAYS" />
|
||||
<VBox fx:id="sha256pane" />
|
||||
</children>
|
||||
</VBox>
|
||||
</content>
|
||||
</TitledPane>
|
||||
<Separator prefWidth="200.0" />
|
||||
<fx:include fx:id="SectionPFS0" source="../NSP/NSPTab.fxml" />
|
||||
</VBox>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue