Fix AesCtrBufferedInputStream bug, add few tests
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Dmitry Isaenko 2022-12-19 21:30:58 +03:00
parent 923fbcb5fc
commit 119b615797
14 changed files with 187 additions and 39 deletions

View file

@ -57,13 +57,26 @@ public class Converter {
} }
public static String intToBinaryString(int value){ public static String intToBinaryString(int value){
return String.format("%32s", Integer.toBinaryString( value )).replace(' ', '0')+" | "+value; return String.format("%32s", Integer.toBinaryString( value )).replace(' ', '0');
} }
public static String longToOctString(long value){ public static String longToOctString(long value){
return String.format("%64s", Long.toBinaryString( value )).replace(' ', '0'); return String.format("%64s", Long.toBinaryString( value )).replace(' ', '0');
} }
public static byte[] hexStringToByteArray(String string){
if (string.length() % 2 != 0)
string = "0" + string;
int resultSize = string.length() / 2;
byte[] resultingArray = new byte[resultSize];
for (int i = 0; i < resultSize; i++){
resultingArray[i] = (byte) Integer.parseInt(string.substring(i*2, i*2+2), 16);
}
return resultingArray;
}
public static byte[] flip(byte[] bytes){ public static byte[] flip(byte[] bytes){
int size = bytes.length; int size = bytes.length;
byte[] ret = new byte[size]; byte[] ret = new byte[size];

View file

@ -154,7 +154,8 @@ public class NCAContent {
ncaOffsetPosition, ncaOffsetPosition,
mediaStartOffset, mediaStartOffset,
mediaEndOffset, mediaEndOffset,
Files.newInputStream(file.toPath())); Files.newInputStream(file.toPath()),
Files.size(file.toPath()));
} }
else else
throw new Exception("Crypto type not supported"); throw new Exception("Crypto type not supported");

View file

@ -286,7 +286,7 @@ public class NCAProvider {
} }
catch (EmptySectionException ignored){} catch (EmptySectionException ignored){}
catch (Exception e){ catch (Exception e){
log.debug("Unable to get NCA Content "+number, e); log.debug("Unable to get NCA Content "+number+" ("+file.getParentFile().getName()+"/"+file.getName()+")", e);
} }
} }
@ -391,4 +391,8 @@ public class NCAProvider {
throw new Exception("NCA Content must be requested in range of 0-3, while 'Section Number "+sectionNumber+"' requested"); throw new Exception("NCA Content must be requested in range of 0-3, while 'Section Number "+sectionNumber+"' requested");
} }
} }
public File getFile() {
return file;
}
} }

View file

@ -196,7 +196,7 @@ public class FileSystemEntry {
fileName = new String(Arrays.copyOfRange(filesMetadataTable, i, i + fileNameLength), StandardCharsets.UTF_8); fileName = new String(Arrays.copyOfRange(filesMetadataTable, i, i + fileNameLength), StandardCharsets.UTF_8);
} }
catch (Exception e){ catch (Exception e){
log.debug("fileName sizes are: "+filesMetadataTable.length+"\t"+i+"\t"+i + fileNameLength+"\t\t"+nextHashTableBucketFileOffset); log.debug("fileName sizes are: "+filesMetadataTable.length+"\t"+i+"\t"+i + fileNameLength+"\t\t"+nextHashTableBucketFileOffset, e);
} }
} }
else { else {

View file

@ -18,6 +18,7 @@
*/ */
package libKonogonka.ctraes; package libKonogonka.ctraes;
import libKonogonka.Converter;
import libKonogonka.RainbowDump; import libKonogonka.RainbowDump;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -30,16 +31,19 @@ public class AesCtrBufferedInputStream extends BufferedInputStream {
private final AesCtrDecryptSimple decryptor; private final AesCtrDecryptSimple decryptor;
private final long mediaOffsetPositionStart; private final long mediaOffsetPositionStart;
private final long mediaOffsetPositionEnd; private final long mediaOffsetPositionEnd;
private final long fileSize;
public AesCtrBufferedInputStream(AesCtrDecryptSimple decryptor, public AesCtrBufferedInputStream(AesCtrDecryptSimple decryptor,
long ncaOffsetPosition, long ncaOffsetPosition,
long mediaStartOffset, long mediaStartOffset,
long mediaEndOffset, long mediaEndOffset,
InputStream inputStream){ InputStream inputStream,
long fileSize){
super(inputStream); super(inputStream);
this.decryptor = decryptor; this.decryptor = decryptor;
this.mediaOffsetPositionStart = ncaOffsetPosition + (mediaStartOffset * 0x200); this.mediaOffsetPositionStart = ncaOffsetPosition + (mediaStartOffset * 0x200);
this.mediaOffsetPositionEnd = ncaOffsetPosition + (mediaEndOffset * 0x200); this.mediaOffsetPositionEnd = ncaOffsetPosition + (mediaEndOffset * 0x200);
this.fileSize = fileSize;
log.trace("\n Offset Position "+ncaOffsetPosition+ log.trace("\n Offset Position "+ncaOffsetPosition+
"\n MediaOffsetPositionStart "+RainbowDump.formatDecHexString(mediaOffsetPositionStart)+ "\n MediaOffsetPositionStart "+RainbowDump.formatDecHexString(mediaOffsetPositionStart)+
@ -75,9 +79,11 @@ public class AesCtrBufferedInputStream extends BufferedInputStream {
fillDecryptedCache(); fillDecryptedCache();
System.arraycopy(decryptedBytes, 0, b, bytesFromFirstBlock+i*0x200, 0x200); System.arraycopy(decryptedBytes, 0, b, bytesFromFirstBlock+i*0x200, 0x200);
} }
//3 // TODO: ONLY IF NON-NULL?? //3
if(fileSize > (pseudoPos+bytesToRead)) {
fillDecryptedCache(); fillDecryptedCache();
System.arraycopy(decryptedBytes, 0, b, bytesFromFirstBlock + middleBlocksCount * 0x200, bytesFromLastBlock); System.arraycopy(decryptedBytes, 0, b, bytesFromFirstBlock + middleBlocksCount * 0x200, bytesFromLastBlock);
}
pseudoPos += bytesToRead; pseudoPos += bytesToRead;
pointerInsideDecryptedSection = bytesFromLastBlock; pointerInsideDecryptedSection = bytesFromLastBlock;
return b.length; return b.length;

View file

@ -70,7 +70,8 @@ public class InFileStreamProducer {
initialOffset, initialOffset,
mediaStartOffset, mediaStartOffset,
mediaEndOffset, mediaEndOffset,
Files.newInputStream(file.toPath())); Files.newInputStream(file.toPath()),
Files.size(file.toPath()));
skipBytesTillBeginning(stream, subOffset); skipBytesTillBeginning(stream, subOffset);
return stream; return stream;
} }

View file

@ -0,0 +1,69 @@
/*
Copyright 2018-2022 Dmitry Isaenko
This file is part of libKonogonka.
libKonogonka is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
libKonogonka is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with libKonogonka. If not, see <https://www.gnu.org/licenses/>.
*/
package libKonogonka.RomFsDecrypted;
import libKonogonka.KeyChainHolder;
import libKonogonka.Tools.NCA.NCAProvider;
import libKonogonka.Tools.NSO.NSO0Provider;
import libKonogonka.Tools.PFS0.PFS0Provider;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
public class ExportNso0FromNcaTest {
private static final String keysFileLocation = "./FilesForTests/prod.keys";
private static final String xci_header_keyFileLocation = "./FilesForTests/xci_header_key.txt";
private static final String ncaFileLocation = "./FilesForTests/nso_container.nca";
private static final String exportDecompressedNsoTo = "/tmp";
@Disabled
@DisplayName("Exports decompressed NSO0 example")
@Test
void nso0Test() throws Exception{
BufferedReader br = new BufferedReader(new FileReader(xci_header_keyFileLocation));
String keyValue = br.readLine();
br.close();
if (keyValue == null)
throw new Exception("Unable to retrieve xci_header_key");
keyValue = keyValue.trim();
KeyChainHolder keyChainHolder = new KeyChainHolder(keysFileLocation, keyValue);
NCAProvider ncaProvider = new NCAProvider(new File(ncaFileLocation), keyChainHolder.getRawKeySet());
PFS0Provider pfs0Provider = ncaProvider.getNCAContentProvider(0).getPfs0();
pfs0Provider.printDebug();
NSO0Provider nso0Provider = new NSO0Provider(pfs0Provider.getStreamProducer(0));
nso0Provider.printDebug();
nso0Provider.exportAsDecompressedNSO0(exportDecompressedNsoTo);
System.out.println("__--++ SDK VERSION ++--__\n"
+ ncaProvider.getSdkVersion()[3]
+"."+ ncaProvider.getSdkVersion()[2]
+"."+ ncaProvider.getSdkVersion()[1]
+"."+ ncaProvider.getSdkVersion()[0]);
}
}

View file

@ -0,0 +1,61 @@
/*
Copyright 2018-2022 Dmitry Isaenko
This file is part of libKonogonka.
libKonogonka is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
libKonogonka is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with libKonogonka. If not, see <https://www.gnu.org/licenses/>.
*/
package libKonogonka.RomFsDecrypted;
import libKonogonka.KeyChainHolder;
import libKonogonka.Tools.NCA.NCAProvider;
import org.junit.jupiter.api.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class NCAProviderSimpleTest {
private static final String keysFileLocation = "./FilesForTests/prod.keys";
private static final String xci_header_keyFileLocation = "./FilesForTests/xci_header_key.txt";
private static final String ncaFileLocation = "./FilesForTests/simple.nca";
//private static final String ncaFileLocation = "./FilesForTests/4pfs.nca";
private static KeyChainHolder keyChainHolder;
private static NCAProvider ncaProvider;
//@Disabled
@Order(1)
@DisplayName("KeyChain loac test")
@Test
void keysChain() throws Exception{
BufferedReader br = new BufferedReader(new FileReader(xci_header_keyFileLocation));
String keyValue = br.readLine();
br.close();
if (keyValue == null)
throw new Exception("Unable to retrieve xci_header_key");
keyValue = keyValue.trim();
keyChainHolder = new KeyChainHolder(keysFileLocation, keyValue);
}
//@Disabled
@Order(2)
@DisplayName("NCA provider test")
@Test
void ncaProvider() throws Exception{
ncaProvider = new NCAProvider(new File(ncaFileLocation), keyChainHolder.getRawKeySet());
}
}

View file

@ -26,18 +26,18 @@ import org.junit.jupiter.api.Test;
import java.io.File; import java.io.File;
public class NSODecompressTest { public class NSODecompressTest {
private static final String ncaExtractedFileLocation = "./FilesForTests/NSO0/main"; private static final String nsoExtractedFileLocation = "./FilesForTests/NSO0/main";
private static final String ncaExtractedFileLocationDec = "./FilesForTests/NSO0/main_d"; private static final String nsoExtractedFileLocationDec = "./FilesForTests/NSO0/main_d";
@Disabled @Disabled
@DisplayName("NSO0 Decompression test") @DisplayName("NSO0 Decompression test")
@Test @Test
void nso0DecompressionTest() throws Exception { void nso0DecompressionTest() throws Exception {
NSO0Provider nso0Provider = new NSO0Provider(new File(ncaExtractedFileLocation)); NSO0Provider nso0Provider = new NSO0Provider(new File(nsoExtractedFileLocation));
//nso0Provider.exportAsDecompressedNSO0("./FilesForTests/NSO0"); //nso0Provider.exportAsDecompressedNSO0("./FilesForTests/NSO0");
nso0Provider.printDebug(); nso0Provider.printDebug();
NSO0Provider nso0Provider1 = new NSO0Provider(new File(ncaExtractedFileLocationDec)); NSO0Provider nso0Provider1 = new NSO0Provider(new File(nsoExtractedFileLocationDec));
nso0Provider1.printDebug(); nso0Provider1.printDebug();
} }
} }

View file

@ -24,7 +24,6 @@ import libKonogonka.Tools.NCA.NCAProvider;
import libKonogonka.Tools.NSO.NSO0Provider; import libKonogonka.Tools.NSO.NSO0Provider;
import libKonogonka.Tools.PFS0.PFS0Provider; import libKonogonka.Tools.PFS0.PFS0Provider;
import libKonogonka.Tools.PFS0.PFS0subFile; import libKonogonka.Tools.PFS0.PFS0subFile;
import libKonogonka.ctraes.AesCtrDecryptSimple;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -74,19 +73,13 @@ public class NSOTest {
} }
} }
long ACBISoffsetPosition;
long ACBISmediaStartOffset;
long ACBISmediaEndOffset;
long offsetPosition;
void nso0Validation() throws Exception{ void nso0Validation() throws Exception{
File nca = new File(ncaFileLocation); File nca = new File(ncaFileLocation);
PFS0subFile[] subfiles = ncaProvider.getNCAContentProvider(0).getPfs0().getHeader().getPfs0subFiles(); PFS0subFile[] subfiles = ncaProvider.getNCAContentProvider(0).getPfs0().getHeader().getPfs0subFiles();
offsetPosition = ncaProvider.getTableEntry0().getMediaStartOffset()*0x200 + long offsetPosition = ncaProvider.getTableEntry0().getMediaStartOffset()*0x200 +
ncaProvider.getNCAContentProvider(0).getPfs0().getRawFileDataStart(); ncaProvider.getNCAContentProvider(0).getPfs0().getRawFileDataStart();
System.out.println("\t============================================================="); System.out.println("\t=============================================================");
System.out.println("\tNCA SIZE: "+ RainbowDump.formatDecHexString(nca.length())); System.out.println("\tNCA SIZE: "+ RainbowDump.formatDecHexString(nca.length()));
System.out.println("\tPFS0 Offset(get) "+RainbowDump.formatDecHexString(ncaProvider.getSectionBlock0().getSuperBlockPFS0().getPfs0offset())); System.out.println("\tPFS0 Offset(get) "+RainbowDump.formatDecHexString(ncaProvider.getSectionBlock0().getSuperBlockPFS0().getPfs0offset()));
@ -102,15 +95,6 @@ public class NSOTest {
} }
System.out.println("\t============================================================="); System.out.println("\t=============================================================");
ACBISoffsetPosition = 0;
ACBISmediaStartOffset = ncaProvider.getTableEntry0().getMediaStartOffset();
ACBISmediaEndOffset = ncaProvider.getTableEntry0().getMediaEndOffset();
AesCtrDecryptSimple decryptSimple = new AesCtrDecryptSimple(
ncaProvider.getDecryptedKey2(),
ncaProvider.getSectionBlock0().getSectionCTR(),
ncaProvider.getTableEntry0().getMediaStartOffset() * 0x200);
PFS0Provider pfs0Provider = ncaProvider.getNCAContentProvider(0).getPfs0(); PFS0Provider pfs0Provider = ncaProvider.getNCAContentProvider(0).getPfs0();
pfs0Provider.printDebug(); pfs0Provider.printDebug();
@ -118,7 +102,7 @@ public class NSOTest {
nso0Provider.printDebug(); nso0Provider.printDebug();
nso0Provider.exportAsDecompressedNSO0("./tmp"); nso0Provider.exportAsDecompressedNSO0("./tmp");
// NPDMProvider npdmProvider = new NPDMProvider(pfs0Provider.getProviderSubFilePipedInpStream(1)); //NPDMProvider npdmProvider = new NPDMProvider(pfs0Provider.getStreamProducer(1));
System.out.println("__--++ SDK VERSION ++--__\n" System.out.println("__--++ SDK VERSION ++--__\n"
+ncaProvider.getSdkVersion()[3] +ncaProvider.getSdkVersion()[3]

View file

@ -32,6 +32,7 @@ import org.junit.jupiter.api.Test;
import java.io.*; import java.io.*;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap; import java.util.HashMap;
public class NSPpfs0EncryptedTest { public class NSPpfs0EncryptedTest {
@ -153,14 +154,16 @@ public class NSPpfs0EncryptedTest {
BufferedOutputStream extractedFileBOS = new BufferedOutputStream(Files.newOutputStream(contentFile.toPath())); BufferedOutputStream extractedFileBOS = new BufferedOutputStream(Files.newOutputStream(contentFile.toPath()));
//--- //---
InputStream is = Files.newInputStream(new File(nspFileLocation).toPath()); //TODO: NOTICE Path filePath = new File(nspFileLocation).toPath();
InputStream is = Files.newInputStream(filePath);
AesCtrBufferedInputStream aesCtrBufferedInputStream = new AesCtrBufferedInputStream( AesCtrBufferedInputStream aesCtrBufferedInputStream = new AesCtrBufferedInputStream(
decryptSimple, decryptSimple,
ACBISoffsetPosition, ACBISoffsetPosition,
ACBISmediaStartOffset, ACBISmediaStartOffset,
ACBISmediaEndOffset, ACBISmediaEndOffset,
is); is,
Files.size(filePath));
//long offsetToSkip = entry.getOffset() + ncaProvider.getNCAContentProvider(0).getPfs0().getRawFileDataStart(); //long offsetToSkip = entry.getOffset() + ncaProvider.getNCAContentProvider(0).getPfs0().getRawFileDataStart();
long offsetToSkip = offsetPosition+entry.getOffset(); long offsetToSkip = offsetPosition+entry.getOffset();

View file

@ -29,6 +29,7 @@ import org.junit.jupiter.api.*;
import java.io.*; import java.io.*;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path;
public class Pfs0EncryptedTest { public class Pfs0EncryptedTest {
private static final String keysFileLocation = "./FilesForTests/prod.keys"; private static final String keysFileLocation = "./FilesForTests/prod.keys";
@ -127,14 +128,16 @@ public class Pfs0EncryptedTest {
BufferedOutputStream extractedFileBOS = new BufferedOutputStream(Files.newOutputStream(contentFile.toPath())); BufferedOutputStream extractedFileBOS = new BufferedOutputStream(Files.newOutputStream(contentFile.toPath()));
//--- //---
InputStream is = Files.newInputStream(new File(ncaFileLocation).toPath()); Path filePath = new File(ncaFileLocation).toPath();
InputStream is = Files.newInputStream(filePath);
AesCtrBufferedInputStream aesCtrBufferedInputStream = new AesCtrBufferedInputStream( AesCtrBufferedInputStream aesCtrBufferedInputStream = new AesCtrBufferedInputStream(
decryptSimple, decryptSimple,
ACBISoffsetPosition, ACBISoffsetPosition,
ACBISmediaStartOffset, ACBISmediaStartOffset,
ACBISmediaEndOffset, ACBISmediaEndOffset,
is); is,
Files.size(filePath));
//long offsetToSkip = entry.getOffset() + ncaProvider.getNCAContentProvider(0).getPfs0().getRawFileDataStart(); //long offsetToSkip = entry.getOffset() + ncaProvider.getNCAContentProvider(0).getPfs0().getRawFileDataStart();
long offsetToSkip = offsetPosition+entry.getOffset(); long offsetToSkip = offsetPosition+entry.getOffset();

View file

@ -29,6 +29,7 @@ import org.junit.jupiter.api.*;
import java.io.*; import java.io.*;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class RomFsEncryptedTest { public class RomFsEncryptedTest {
@ -136,14 +137,16 @@ public class RomFsEncryptedTest {
BufferedOutputStream extractedFileBOS = new BufferedOutputStream(Files.newOutputStream(contentFile.toPath())); BufferedOutputStream extractedFileBOS = new BufferedOutputStream(Files.newOutputStream(contentFile.toPath()));
//--- //---
InputStream is = Files.newInputStream(new File(ncaFileLocation).toPath()); Path filePath = new File(ncaFileLocation).toPath();
InputStream is = Files.newInputStream(filePath);
AesCtrBufferedInputStream aesCtrBufferedInputStream = new AesCtrBufferedInputStream( AesCtrBufferedInputStream aesCtrBufferedInputStream = new AesCtrBufferedInputStream(
decryptSimple, decryptSimple,
ACBISoffsetPosition, ACBISoffsetPosition,
ACBISmediaStartOffset, ACBISmediaStartOffset,
ACBISmediaEndOffset, ACBISmediaEndOffset,
is); is,
Files.size(filePath));
long skipBytes = entry.getOffset() long skipBytes = entry.getOffset()
+ncaProvider.getTableEntry1().getMediaStartOffset()*0x200 +ncaProvider.getTableEntry1().getMediaStartOffset()*0x200