CSS updates
PFS0Encrypted logic intermediate results
This commit is contained in:
parent
da423152fc
commit
56ffca7b92
9 changed files with 300 additions and 62 deletions
|
@ -1,12 +1,10 @@
|
||||||
package konogonka.Tools.NCA;
|
package konogonka.Tools.NCA;
|
||||||
|
|
||||||
import konogonka.LoperConverter;
|
|
||||||
import konogonka.RainbowHexDump;
|
|
||||||
import konogonka.Tools.NCA.NCASectionTableBlock.NCASectionBlock;
|
import konogonka.Tools.NCA.NCASectionTableBlock.NCASectionBlock;
|
||||||
import konogonka.Tools.PFS0.IPFS0Provider;
|
import konogonka.Tools.PFS0.IPFS0Provider;
|
||||||
import konogonka.Tools.PFS0.PFS0EncryptedProvider;
|
import konogonka.Tools.PFS0.PFS0EncryptedProvider;
|
||||||
import konogonka.Tools.PFS0.PFS0Provider;
|
import konogonka.Tools.PFS0.PFS0Provider;
|
||||||
import konogonka.ctraes.AesCtr;
|
import konogonka.ctraes.AesCtrDecryptSimple;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
@ -94,7 +92,7 @@ public class NCAContentPFS0 {
|
||||||
long abosluteOffsetPosition = offsetPosition + (mediaStartOffset * 0x200);
|
long abosluteOffsetPosition = offsetPosition + (mediaStartOffset * 0x200);
|
||||||
raf.seek(abosluteOffsetPosition);
|
raf.seek(abosluteOffsetPosition);
|
||||||
|
|
||||||
AesCtrDecryptor decryptor = new AesCtrDecryptor(decryptedKey, ncaSectionBlock.getSectionCTR(), mediaStartOffset * 0x200);
|
AesCtrDecryptSimple decryptor = new AesCtrDecryptSimple(decryptedKey, ncaSectionBlock.getSectionCTR(), mediaStartOffset * 0x200);
|
||||||
|
|
||||||
byte[] encryptedBlock;
|
byte[] encryptedBlock;
|
||||||
byte[] dectyptedBlock;
|
byte[] dectyptedBlock;
|
||||||
|
@ -106,7 +104,6 @@ public class NCAContentPFS0 {
|
||||||
Thread pThread = new Thread(new ParseThread(
|
Thread pThread = new Thread(new ParseThread(
|
||||||
streamInp,
|
streamInp,
|
||||||
ncaSectionBlock.getSuperBlockPFS0().getPfs0offset(),
|
ncaSectionBlock.getSuperBlockPFS0().getPfs0offset(),
|
||||||
ncaSectionBlock.getSuperBlockPFS0().getPfs0size(),
|
|
||||||
ncaSectionBlock.getSuperBlockPFS0().getHashTableOffset(),
|
ncaSectionBlock.getSuperBlockPFS0().getHashTableOffset(),
|
||||||
ncaSectionBlock.getSuperBlockPFS0().getHashTableSize()
|
ncaSectionBlock.getSuperBlockPFS0().getHashTableSize()
|
||||||
));
|
));
|
||||||
|
@ -129,39 +126,46 @@ public class NCAContentPFS0 {
|
||||||
pThread.join();
|
pThread.join();
|
||||||
streamOut.close();
|
streamOut.close();
|
||||||
raf.close();
|
raf.close();
|
||||||
}
|
// TODO: re-write
|
||||||
/*
|
if (pfs0 != null){
|
||||||
* Simplify decryption of the CTR
|
PFS0EncryptedProvider pfs0enc = (PFS0EncryptedProvider)pfs0;
|
||||||
* */
|
pfs0enc.setMeta(
|
||||||
private class AesCtrDecryptor{
|
offsetPosition,
|
||||||
|
file,
|
||||||
private long realMediaOffset;
|
decryptedKey,
|
||||||
byte[] IVarray;
|
ncaSectionBlock.getSectionCTR(),
|
||||||
private AesCtr aesCtr;
|
mediaStartOffset,
|
||||||
|
mediaEndOffset
|
||||||
AesCtrDecryptor(byte[] decryptedKey, byte[] sectionCTR, long realMediaOffset) throws Exception{
|
);
|
||||||
this.realMediaOffset = realMediaOffset;
|
|
||||||
aesCtr = new AesCtr(decryptedKey);
|
|
||||||
// IV for CTR == 16 bytes
|
|
||||||
IVarray = new byte[0x10];
|
|
||||||
// Populate first 8 bytes taken from Header's section Block CTR
|
|
||||||
System.arraycopy(LoperConverter.flip(sectionCTR), 0x0, IVarray, 0x0, 0x8);
|
|
||||||
}
|
}
|
||||||
|
//****************************************___DEBUG___*******************************************************
|
||||||
|
//*
|
||||||
|
File contentFile = new File("/tmp/decryptedNCA0block.pfs0");
|
||||||
|
BufferedOutputStream extractedFileOS = new BufferedOutputStream(new FileOutputStream(contentFile));
|
||||||
|
|
||||||
public byte[] dectyptNext(byte[] enctyptedBlock) throws Exception{
|
raf = new RandomAccessFile(file, "r");
|
||||||
updateIV(realMediaOffset);
|
raf.seek(abosluteOffsetPosition);
|
||||||
byte[] decryptedBlock = aesCtr.decrypt(enctyptedBlock, IVarray);
|
decryptor = new AesCtrDecryptSimple(decryptedKey, ncaSectionBlock.getSectionCTR(), mediaStartOffset * 0x200);
|
||||||
realMediaOffset += 0x200;
|
|
||||||
return decryptedBlock;
|
for (int i = 0; i < mediaBlockSize; i++){
|
||||||
}
|
encryptedBlock = new byte[0x200];
|
||||||
// Populate last 8 bytes calculated. Thanks hactool project!
|
if (raf.read(encryptedBlock) != -1){
|
||||||
private void updateIV(long offset){
|
//dectyptedBlock = aesCtr.decrypt(encryptedBlock);
|
||||||
offset >>= 4;
|
dectyptedBlock = decryptor.dectyptNext(encryptedBlock);
|
||||||
for (int i = 0; i < 0x8; i++){
|
// Writing decrypted data to pipe
|
||||||
IVarray[0x10-i-1] = (byte)(offset & 0xff); // Note: issues could be here
|
try {
|
||||||
offset >>= 8;
|
extractedFileOS.write(dectyptedBlock);
|
||||||
|
}
|
||||||
|
catch (IOException e){
|
||||||
|
System.out.println("Exception @extract");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
extractedFileOS.close();
|
||||||
|
raf.close();
|
||||||
|
System.out.println("@extract done");
|
||||||
|
//*//******************************************************************************************************/
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Since we representing decrypted data as stream (it's easier to look on it this way),
|
* Since we representing decrypted data as stream (it's easier to look on it this way),
|
||||||
|
@ -175,15 +179,14 @@ public class NCAContentPFS0 {
|
||||||
long hashTableSize;
|
long hashTableSize;
|
||||||
long hashTableRecordsCount;
|
long hashTableRecordsCount;
|
||||||
long pfs0offset;
|
long pfs0offset;
|
||||||
long pfs0size;
|
|
||||||
|
|
||||||
ParseThread(PipedInputStream pipedInputStream, long pfs0offset, long pfs0size, long hashTableOffset, long hashTableSize){
|
|
||||||
|
ParseThread(PipedInputStream pipedInputStream, long pfs0offset, long hashTableOffset, long hashTableSize){
|
||||||
this.pipedInputStream = pipedInputStream;
|
this.pipedInputStream = pipedInputStream;
|
||||||
this.hashTableOffset = hashTableOffset;
|
this.hashTableOffset = hashTableOffset;
|
||||||
this.hashTableSize = hashTableSize;
|
this.hashTableSize = hashTableSize;
|
||||||
this.hashTableRecordsCount = hashTableSize / 0x20;
|
this.hashTableRecordsCount = hashTableSize / 0x20;
|
||||||
this.pfs0offset = pfs0offset;
|
this.pfs0offset = pfs0offset;
|
||||||
this.pfs0size = pfs0size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -220,7 +223,7 @@ public class NCAContentPFS0 {
|
||||||
counter += toSkip;
|
counter += toSkip;
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
pfs0 = new PFS0EncryptedProvider(pipedInputStream);
|
pfs0 = new PFS0EncryptedProvider(pipedInputStream, counter);
|
||||||
pipedInputStream.close();
|
pipedInputStream.close();
|
||||||
}
|
}
|
||||||
catch (Exception e){
|
catch (Exception e){
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package konogonka.Tools.PFS0;
|
package konogonka.Tools.PFS0;
|
||||||
|
|
||||||
public interface IPFS0Provider {
|
public interface IPFS0Provider {
|
||||||
|
boolean isEncrypted();
|
||||||
String getMagic();
|
String getMagic();
|
||||||
int getFilesCount();
|
int getFilesCount();
|
||||||
int getStringTableSize();
|
int getStringTableSize();
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
package konogonka.Tools.PFS0;
|
package konogonka.Tools.PFS0;
|
||||||
|
|
||||||
import java.io.PipedInputStream;
|
import konogonka.ctraes.AesCtrDecryptSimple;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static konogonka.LoperConverter.getLEint;
|
import static konogonka.LoperConverter.*;
|
||||||
import static konogonka.LoperConverter.getLElong;
|
|
||||||
|
|
||||||
public class PFS0EncryptedProvider implements IPFS0Provider{
|
public class PFS0EncryptedProvider implements IPFS0Provider{
|
||||||
private long rawFileDataStart; // If -1 then this PFS0 located @ encrypted region
|
private long rawFileDataStart; // For this class is pointing to data start position relative to media block start
|
||||||
|
|
||||||
private String magic;
|
private String magic;
|
||||||
private int filesCount;
|
private int filesCount;
|
||||||
|
@ -17,15 +18,145 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
|
||||||
private PFS0subFile[] pfs0subFiles;
|
private PFS0subFile[] pfs0subFiles;
|
||||||
|
|
||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
/*
|
|
||||||
absOffsetPosOfMediaBlock
|
|
||||||
|
|
||||||
Counter - PFS0 Position
|
private long rawBlockDataStart;
|
||||||
mediaBlockSize - PFS0 Subsustem Size
|
|
||||||
* */
|
private PFS0DecryptedStreamProvider pfs0DecryptedStreamProvider;
|
||||||
|
// Let's do some fuck
|
||||||
|
private class PFS0DecryptedStreamProvider{
|
||||||
|
private long mediaStartOffset; // * 0x200
|
||||||
|
private long mediaEndOffset;
|
||||||
|
|
||||||
|
|
||||||
|
private RandomAccessFile raf;
|
||||||
|
private PipedOutputStream streamOut;
|
||||||
|
private PipedInputStream streamInp;
|
||||||
|
private AesCtrDecryptSimple aesCtrDecryptSimple; // if null, then exception happened.
|
||||||
|
|
||||||
|
public PipedInputStream getPipedInputStream(){ return streamInp; }
|
||||||
|
|
||||||
|
|
||||||
|
public void getStarted(PFS0subFile subFile) throws Exception{
|
||||||
|
|
||||||
|
System.out.println("rawBlockDataStart: "+rawBlockDataStart);
|
||||||
|
System.out.println("Skip blocks: "+rawBlockDataStart/0x200); // aesCtrDecryptSimple.skip(THIS)
|
||||||
|
System.out.println("Skip bytes: "+ (rawBlockDataStart-(rawBlockDataStart/0x200)*0x200)); // write to stream after skiping THIS
|
||||||
|
|
||||||
|
// DBG START
|
||||||
|
File contentFile = new File("/tmp/pfs0-NCA0block.pfs0");
|
||||||
|
BufferedOutputStream extractedFileOS = new BufferedOutputStream(new FileOutputStream(contentFile));
|
||||||
|
// DBG END
|
||||||
|
long mediaBlockSize = mediaEndOffset - mediaStartOffset;
|
||||||
|
byte[] encryptedBlock;
|
||||||
|
byte[] dectyptedBlock;
|
||||||
|
|
||||||
|
// Skip full-size blocks of 512 bytes that we don't need and start decryption from required one
|
||||||
|
long startFromBlock = rawBlockDataStart/0x200;
|
||||||
|
if (startFromBlock > 0) {
|
||||||
|
aesCtrDecryptSimple.skipNext(startFromBlock);
|
||||||
|
raf.seek(raf.getFilePointer() + (startFromBlock*0x200));
|
||||||
|
}
|
||||||
|
// Since our data could be located in position with some offset from the decrypted block, let's skip bytes left
|
||||||
|
int skipBytes = (int)(rawBlockDataStart-(rawBlockDataStart/0x200)*0x200);
|
||||||
|
if (skipBytes > 0){
|
||||||
|
encryptedBlock = new byte[0x200];
|
||||||
|
if (raf.read(encryptedBlock) != -1){
|
||||||
|
dectyptedBlock = aesCtrDecryptSimple.dectyptNext(encryptedBlock);
|
||||||
|
try {
|
||||||
|
// DBG START
|
||||||
|
extractedFileOS.write(dectyptedBlock, skipBytes, 0x200-skipBytes);
|
||||||
|
// DBG END
|
||||||
|
//streamOut.write(dectyptedBlock, skipBytes, 0x200);
|
||||||
|
}
|
||||||
|
catch (IOException e){
|
||||||
|
System.out.println("Exception @extract 1st bock");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startFromBlock++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (long i = startFromBlock; i < mediaBlockSize; i++){
|
||||||
|
encryptedBlock = new byte[0x200];
|
||||||
|
if (raf.read(encryptedBlock) != -1){
|
||||||
|
//dectyptedBlock = aesCtr.decrypt(encryptedBlock);
|
||||||
|
dectyptedBlock = aesCtrDecryptSimple.dectyptNext(encryptedBlock);
|
||||||
|
// Writing decrypted data to pipe
|
||||||
|
try {
|
||||||
|
// DBG START
|
||||||
|
extractedFileOS.write(dectyptedBlock);
|
||||||
|
// DBG END
|
||||||
|
//streamOut.write(dectyptedBlock);
|
||||||
|
}
|
||||||
|
catch (IOException e){
|
||||||
|
System.out.println("Exception @extract");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DBG START
|
||||||
|
extractedFileOS.close();
|
||||||
|
// DBG END
|
||||||
|
}
|
||||||
|
|
||||||
|
PFS0DecryptedStreamProvider(File file,
|
||||||
|
long rawBlockDataStart,
|
||||||
|
long offsetPositionInFile,
|
||||||
|
byte[] key,
|
||||||
|
byte[] sectionCTR,
|
||||||
|
long mediaStartOffset,
|
||||||
|
long mediaEndOffset
|
||||||
|
){
|
||||||
|
this.mediaStartOffset = mediaStartOffset;
|
||||||
|
this.mediaEndOffset = mediaEndOffset;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.raf = new RandomAccessFile(file, "r");
|
||||||
|
this.raf.seek(offsetPositionInFile + (mediaStartOffset * 0x200));
|
||||||
|
this.aesCtrDecryptSimple = new AesCtrDecryptSimple(key, sectionCTR, mediaStartOffset * 0x200);
|
||||||
|
|
||||||
|
this.streamOut = new PipedOutputStream();
|
||||||
|
this.streamInp = new PipedInputStream(streamOut);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: simplify
|
||||||
|
public void setMeta(
|
||||||
|
long offsetPositionInFile,
|
||||||
|
File fileWithEncPFS0,
|
||||||
|
byte[] key,
|
||||||
|
byte[] sectionCTR,
|
||||||
|
long mediaStartOffset,
|
||||||
|
long mediaEndOffset
|
||||||
|
){
|
||||||
|
this.pfs0DecryptedStreamProvider = new PFS0DecryptedStreamProvider(
|
||||||
|
fileWithEncPFS0,
|
||||||
|
rawBlockDataStart,
|
||||||
|
offsetPositionInFile,
|
||||||
|
key,
|
||||||
|
sectionCTR,
|
||||||
|
mediaStartOffset,
|
||||||
|
mediaEndOffset
|
||||||
|
);
|
||||||
|
try{
|
||||||
|
pfs0DecryptedStreamProvider.getStarted(pfs0subFiles[0]);
|
||||||
|
}
|
||||||
|
catch (Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
|
public PFS0EncryptedProvider(PipedInputStream pipedInputStream,
|
||||||
|
long pfs0offsetPosition
|
||||||
|
) throws Exception{
|
||||||
|
// pfs0offsetPosition is a position relative to Media block. Lets add pfs0 'header's' bytes count and get raw data start position in media block
|
||||||
|
rawFileDataStart = -1; // Set -1 for PFS0EncryptedProvider
|
||||||
|
// Detect raw data start position using next var
|
||||||
|
rawBlockDataStart = pfs0offsetPosition;
|
||||||
|
|
||||||
public PFS0EncryptedProvider(PipedInputStream pipedInputStream) throws Exception{
|
|
||||||
byte[] fileStartingBytes = new byte[0x10];
|
byte[] fileStartingBytes = new byte[0x10];
|
||||||
// Read PFS0Provider, files count, header, padding (4 zero bytes)
|
// Read PFS0Provider, files count, header, padding (4 zero bytes)
|
||||||
|
|
||||||
|
@ -36,6 +167,8 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
|
||||||
}
|
}
|
||||||
fileStartingBytes[i] = (byte)currentByte;
|
fileStartingBytes[i] = (byte)currentByte;
|
||||||
}
|
}
|
||||||
|
// Update position
|
||||||
|
rawBlockDataStart += 0x10;
|
||||||
// Check PFS0Provider
|
// Check PFS0Provider
|
||||||
magic = new String(fileStartingBytes, 0x0, 0x4, StandardCharsets.US_ASCII);
|
magic = new String(fileStartingBytes, 0x0, 0x4, StandardCharsets.US_ASCII);
|
||||||
if (! magic.equals("PFS0")){
|
if (! magic.equals("PFS0")){
|
||||||
|
@ -73,6 +206,8 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
|
||||||
sizesSubFiles[i] = getLElong(fileEntryTable, 0x8);
|
sizesSubFiles[i] = getLElong(fileEntryTable, 0x8);
|
||||||
strTableOffsets[i] = getLEint(fileEntryTable, 0x10);
|
strTableOffsets[i] = getLEint(fileEntryTable, 0x10);
|
||||||
zeroBytes[i] = Arrays.copyOfRange(fileEntryTable, 0x14, 0x18);
|
zeroBytes[i] = Arrays.copyOfRange(fileEntryTable, 0x14, 0x18);
|
||||||
|
// Update position
|
||||||
|
rawBlockDataStart += 0x18;
|
||||||
}
|
}
|
||||||
//**********************************************************************************************************
|
//**********************************************************************************************************
|
||||||
// In here pointer in front of String table
|
// In here pointer in front of String table
|
||||||
|
@ -86,6 +221,8 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
|
||||||
}
|
}
|
||||||
stringTbl[i] = (byte)currentByte;
|
stringTbl[i] = (byte)currentByte;
|
||||||
}
|
}
|
||||||
|
// Update position
|
||||||
|
rawBlockDataStart += stringTableSize;
|
||||||
|
|
||||||
for (int i=0; i < filesCount; i++){
|
for (int i=0; i < filesCount; i++){
|
||||||
int j = 0;
|
int j = 0;
|
||||||
|
@ -101,14 +238,21 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
|
||||||
zeroBytes[i]
|
zeroBytes[i]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
rawFileDataStart = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEncrypted() { return true; }
|
||||||
|
@Override
|
||||||
public String getMagic() { return magic; }
|
public String getMagic() { return magic; }
|
||||||
|
@Override
|
||||||
public int getFilesCount() { return filesCount; }
|
public int getFilesCount() { return filesCount; }
|
||||||
|
@Override
|
||||||
public int getStringTableSize() { return stringTableSize; }
|
public int getStringTableSize() { return stringTableSize; }
|
||||||
|
@Override
|
||||||
public byte[] getPadding() { return padding; }
|
public byte[] getPadding() { return padding; }
|
||||||
|
@Override
|
||||||
public long getRawFileDataStart() { return rawFileDataStart; }
|
public long getRawFileDataStart() { return rawFileDataStart; }
|
||||||
|
@Override
|
||||||
public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; }
|
public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,11 +93,18 @@ public class PFS0Provider implements IPFS0Provider{
|
||||||
raf.close();
|
raf.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEncrypted() { return false; }
|
||||||
|
@Override
|
||||||
public String getMagic() { return magic; }
|
public String getMagic() { return magic; }
|
||||||
|
@Override
|
||||||
public int getFilesCount() { return filesCount; }
|
public int getFilesCount() { return filesCount; }
|
||||||
|
@Override
|
||||||
public int getStringTableSize() { return stringTableSize; }
|
public int getStringTableSize() { return stringTableSize; }
|
||||||
|
@Override
|
||||||
public byte[] getPadding() { return padding; }
|
public byte[] getPadding() { return padding; }
|
||||||
|
@Override
|
||||||
public long getRawFileDataStart() { return rawFileDataStart; }
|
public long getRawFileDataStart() { return rawFileDataStart; }
|
||||||
|
@Override
|
||||||
public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; }
|
public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; }
|
||||||
}
|
}
|
||||||
|
|
45
src/main/java/konogonka/ctraes/AesCtrDecryptSimple.java
Normal file
45
src/main/java/konogonka/ctraes/AesCtrDecryptSimple.java
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package konogonka.ctraes;
|
||||||
|
|
||||||
|
import konogonka.LoperConverter;
|
||||||
|
/**
|
||||||
|
* Simplify decryption of the CTR
|
||||||
|
*/
|
||||||
|
public class AesCtrDecryptSimple {
|
||||||
|
|
||||||
|
private long realMediaOffset;
|
||||||
|
private byte[] IVarray;
|
||||||
|
private AesCtr aesCtr;
|
||||||
|
|
||||||
|
public AesCtrDecryptSimple(byte[] key, byte[] sectionCTR, long realMediaOffset) throws Exception{
|
||||||
|
this.realMediaOffset = realMediaOffset;
|
||||||
|
aesCtr = new AesCtr(key);
|
||||||
|
// IV for CTR == 16 bytes
|
||||||
|
IVarray = new byte[0x10];
|
||||||
|
// Populate first 8 bytes taken from Header's section Block CTR
|
||||||
|
System.arraycopy(LoperConverter.flip(sectionCTR), 0x0, IVarray, 0x0, 0x8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void skipNext(){
|
||||||
|
realMediaOffset += 0x200;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void skipNext(long blocksNum){
|
||||||
|
if (blocksNum > 0)
|
||||||
|
realMediaOffset += blocksNum * 0x200;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] dectyptNext(byte[] enctyptedBlock) throws Exception{
|
||||||
|
updateIV(realMediaOffset);
|
||||||
|
byte[] decryptedBlock = aesCtr.decrypt(enctyptedBlock, IVarray);
|
||||||
|
realMediaOffset += 0x200;
|
||||||
|
return decryptedBlock;
|
||||||
|
}
|
||||||
|
// Populate last 8 bytes calculated. Thanks hactool project!
|
||||||
|
private void updateIV(long offset){
|
||||||
|
offset >>= 4;
|
||||||
|
for (int i = 0; i < 0x8; i++){
|
||||||
|
IVarray[0x10-i-1] = (byte)(offset & 0xff); // Note: issues could be here
|
||||||
|
offset >>= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -407,7 +407,7 @@
|
||||||
</AnchorPane>
|
</AnchorPane>
|
||||||
<AnchorPane styleClass="customGrid" GridPane.columnIndex="2" GridPane.rowIndex="12">
|
<AnchorPane styleClass="customGrid" GridPane.columnIndex="2" GridPane.rowIndex="12">
|
||||||
<children>
|
<children>
|
||||||
<Label text="Rights ID (Ticket)" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
<Label text="Rights ID (title.keys key-name to decrypt)" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets left="5.0" right="5.0" />
|
<Insets left="5.0" right="5.0" />
|
||||||
</padding>
|
</padding>
|
||||||
|
|
|
@ -15,9 +15,9 @@
|
||||||
<ListView fx:id="listView" onKeyPressed="#listKeyPressed" HBox.hgrow="ALWAYS" />
|
<ListView fx:id="listView" onKeyPressed="#listKeyPressed" HBox.hgrow="ALWAYS" />
|
||||||
<VBox spacing="5.0">
|
<VBox spacing="5.0">
|
||||||
<children>
|
<children>
|
||||||
<Button mnemonicParsing="false" onAction="#removeRecord">
|
<Button mnemonicParsing="false" onAction="#removeRecord" styleClass="buttonRed">
|
||||||
<graphic>
|
<graphic>
|
||||||
<SVGPath content="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" fill="#cc0101" />
|
<SVGPath content="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" />
|
||||||
</graphic>
|
</graphic>
|
||||||
</Button>
|
</Button>
|
||||||
</children>
|
</children>
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
<TextField fx:id="newRecordValue" HBox.hgrow="ALWAYS" />
|
<TextField fx:id="newRecordValue" HBox.hgrow="ALWAYS" />
|
||||||
<Button mnemonicParsing="false" onAction="#addNewRecord">
|
<Button mnemonicParsing="false" onAction="#addNewRecord">
|
||||||
<graphic>
|
<graphic>
|
||||||
<SVGPath content="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z" fill="#007f08" />
|
<SVGPath content="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z" />
|
||||||
</graphic>
|
</graphic>
|
||||||
</Button>
|
</Button>
|
||||||
</children>
|
</children>
|
||||||
|
|
|
@ -112,7 +112,7 @@
|
||||||
<ButtonBar>
|
<ButtonBar>
|
||||||
<buttons>
|
<buttons>
|
||||||
<Button fx:id="okBtn" mnemonicParsing="false" text="Ok" />
|
<Button fx:id="okBtn" mnemonicParsing="false" text="Ok" />
|
||||||
<Button fx:id="cancelBtn" mnemonicParsing="false" text="Cancel" />
|
<Button fx:id="cancelBtn" mnemonicParsing="false" styleClass="buttonRed" text="Cancel" />
|
||||||
</buttons>
|
</buttons>
|
||||||
<padding>
|
<padding>
|
||||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
-fx-background-color: #ffffff;
|
-fx-background-color: #ffffff;
|
||||||
-fx-border-color: #0d98ba;
|
-fx-border-color: #0d98ba;
|
||||||
-fx-background-radius: 5;
|
-fx-background-radius: 5;
|
||||||
-fx-border-radius: 2;
|
-fx-border-radius: 5;
|
||||||
-fx-border-width: 2;
|
-fx-border-width: 2;
|
||||||
-fx-text-fill: #2c2c2c;
|
-fx-text-fill: #2c2c2c;
|
||||||
}
|
}
|
||||||
|
@ -59,13 +59,51 @@
|
||||||
-fx-border-width: 2;
|
-fx-border-width: 2;
|
||||||
-fx-text-fill: #ffffff;
|
-fx-text-fill: #ffffff;
|
||||||
}
|
}
|
||||||
|
// -========================+ Button RED =====================-
|
||||||
|
.buttonRed SVGPath{
|
||||||
|
-fx-fill: #e34234;
|
||||||
|
}
|
||||||
|
.buttonRed{
|
||||||
|
-fx-background-color: #ffffff;
|
||||||
|
-fx-border-color: #e34234;
|
||||||
|
-fx-background-radius: 5;
|
||||||
|
-fx-border-radius: 5;
|
||||||
|
-fx-border-width: 2;
|
||||||
|
-fx-text-fill: #2c2c2c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonRed:hover, .buttonRed:focused:hover{
|
||||||
|
-fx-background-color: #e34234;
|
||||||
|
-fx-border-color: #e34234;
|
||||||
|
-fx-background-radius: 5;
|
||||||
|
-fx-border-radius: 5;
|
||||||
|
-fx-border-width: 2;
|
||||||
|
-fx-text-fill: #ffffff;
|
||||||
|
}
|
||||||
|
.buttonRed:focused{
|
||||||
|
-fx-background-color: #d0d0d0;
|
||||||
|
-fx-border-color: #cc0605;
|
||||||
|
-fx-background-radius: 5;
|
||||||
|
-fx-border-radius: 5;
|
||||||
|
-fx-border-width: 2;
|
||||||
|
-fx-text-fill: #2c2c2c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonRed:pressed, .buttonRed:pressed:hover{
|
||||||
|
-fx-background-color: #cc0605;
|
||||||
|
-fx-border-color: #cc0605;
|
||||||
|
-fx-background-radius: 5;
|
||||||
|
-fx-border-radius: 5;
|
||||||
|
-fx-border-width: 2;
|
||||||
|
-fx-text-fill: #ffffff;
|
||||||
|
}
|
||||||
// -========================+ TextArea =====================-
|
// -========================+ TextArea =====================-
|
||||||
.text-area{
|
.text-area{
|
||||||
-fx-background-color: transparent;
|
-fx-background-color: transparent;
|
||||||
-fx-control-inner-background: #fefefe;
|
-fx-control-inner-background: #fefefe;
|
||||||
-fx-font-family: "Noto Mono";
|
-fx-font-family: "Noto Mono";
|
||||||
-fx-background-radius: 3;
|
-fx-background-radius: 3;
|
||||||
-fx-border-color: #ffdab9;
|
-fx-border-color: #0d98ba;
|
||||||
-fx-border-radius: 3;
|
-fx-border-radius: 3;
|
||||||
-fx-border-width: 2;
|
-fx-border-width: 2;
|
||||||
-fx-text-fill: #2c2c2c;
|
-fx-text-fill: #2c2c2c;
|
||||||
|
@ -185,8 +223,8 @@
|
||||||
.tab-pane .tab SVGPath{
|
.tab-pane .tab SVGPath{
|
||||||
-fx-fill: #2c2c2c; // OK
|
-fx-fill: #2c2c2c; // OK
|
||||||
}
|
}
|
||||||
.tab-pane .tab:selected SVGPath{
|
.tab-pane .tab:selected SVGPath, .tab-pane .tab:hover SVGPath{
|
||||||
-fx-fill: #1dacd6; // OK
|
-fx-fill: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-pane .tab .label{
|
.tab-pane .tab .label{
|
||||||
|
@ -237,7 +275,7 @@
|
||||||
-fx-background-position: center;
|
-fx-background-position: center;
|
||||||
-fx-background-repeat: no-repeat;
|
-fx-background-repeat: no-repeat;
|
||||||
-fx-background-radius: 3;
|
-fx-background-radius: 3;
|
||||||
-fx-border-color: #ffdab9;
|
-fx-border-color: #0d98ba;
|
||||||
-fx-border-radius: 3;
|
-fx-border-radius: 3;
|
||||||
-fx-border-width: 2;
|
-fx-border-width: 2;
|
||||||
}
|
}
|
||||||
|
@ -329,7 +367,7 @@
|
||||||
|
|
||||||
.titled-pane > .title
|
.titled-pane > .title
|
||||||
{
|
{
|
||||||
-fx-background-color: #fff8e7;
|
-fx-background-color: #efebe6;
|
||||||
-fx-background-insets: 0, 1, 2;
|
-fx-background-insets: 0, 1, 2;
|
||||||
-fx-background-radius: 5 5 0 0, 4 4 0 0, 3 3 0 0;
|
-fx-background-radius: 5 5 0 0, 4 4 0 0, 3 3 0 0;
|
||||||
-fx-padding: 3 11 5 11;
|
-fx-padding: 3 11 5 11;
|
||||||
|
|
Loading…
Reference in a new issue