From 6e4e4dd3e7bea4a15bf136157d9d9a8a9f9e5db1 Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Sat, 21 Jan 2023 13:47:33 +0300 Subject: [PATCH] AES CTR refactor, simplify, unify --- src/main/java/libKonogonka/Converter.java | 15 +- .../libKonogonka/Tools/ISuperProvider.java | 2 +- .../libKonogonka/Tools/NCA/NCAContent.java | 6 +- .../NCA/NCASectionTableBlock/NcaFsHeader.java | 3 +- .../libKonogonka/Tools/NPDM/NPDMProvider.java | 2 +- .../libKonogonka/Tools/NSO/NSO0Provider.java | 2 +- .../libKonogonka/Tools/NSO/NSO0Unpacker.java | 2 +- .../libKonogonka/Tools/PFS0/PFS0Provider.java | 4 +- .../Tools/RomFs/RomFsConstruct.java | 2 +- .../Tools/RomFs/RomFsProvider.java | 2 +- .../libKonogonka/Tools/XCI/HFS0Provider.java | 4 +- .../Tools/other/System2/System2Header.java | 2 +- .../Tools/other/System2/System2Provider.java | 4 +- .../other/System2/ini1/Ini1Provider.java | 2 +- .../other/System2/ini1/KIP1Provider.java | 2 +- .../other/System2/ini1/Kip1Unpacker.java | 2 +- .../AesCtrBufferedInputStream.java | 84 ++++-- .../libKonogonka/aesctr/AesCtrDecrypt.java | 32 +++ .../AesCtrDecryptClassic.java | 50 +--- .../aesctr/AesCtrDecryptForMediaBlocks.java | 66 +++++ .../InFileStreamClassicProducer.java | 5 +- .../InFileStreamProducer.java | 2 +- .../ctraes/AesCtrDecryptForMediaBlocks.java | 93 ------- .../AesCtrClassicBufferedInputStream.java | 248 ------------------ .../ctraesclassic/AesCtrStream.java | 60 ----- .../package2/Ini1ExtractTest.java | 2 +- .../package2/KernelBinExtractTest.java | 3 +- .../package2/Kip1ExtractDecompressedTest.java | 2 +- .../package2/Kip1ExtractTest.java | 2 +- .../unsorted/NSPpfs0EncryptedTest.java | 4 +- .../unsorted/Pfs0EncryptedTest.java | 4 +- .../unsorted/RomFsEncryptedTest.java | 4 +- 32 files changed, 204 insertions(+), 513 deletions(-) rename src/main/java/libKonogonka/{ctraes => aesctr}/AesCtrBufferedInputStream.java (77%) create mode 100644 src/main/java/libKonogonka/aesctr/AesCtrDecrypt.java rename src/main/java/libKonogonka/{ctraesclassic => aesctr}/AesCtrDecryptClassic.java (60%) create mode 100644 src/main/java/libKonogonka/aesctr/AesCtrDecryptForMediaBlocks.java rename src/main/java/libKonogonka/{ctraesclassic => aesctr}/InFileStreamClassicProducer.java (97%) rename src/main/java/libKonogonka/{ctraes => aesctr}/InFileStreamProducer.java (99%) delete mode 100644 src/main/java/libKonogonka/ctraes/AesCtrDecryptForMediaBlocks.java delete mode 100644 src/main/java/libKonogonka/ctraesclassic/AesCtrClassicBufferedInputStream.java delete mode 100644 src/main/java/libKonogonka/ctraesclassic/AesCtrStream.java diff --git a/src/main/java/libKonogonka/Converter.java b/src/main/java/libKonogonka/Converter.java index a52ed93..053e6a0 100644 --- a/src/main/java/libKonogonka/Converter.java +++ b/src/main/java/libKonogonka/Converter.java @@ -89,16 +89,13 @@ public class Converter { } 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); + int len = string.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(string.charAt(i), 16) << 4) + + Character.digit(string.charAt(i+1), 16)); } - return resultingArray; + return data; } public static byte[] flip(byte[] bytes){ diff --git a/src/main/java/libKonogonka/Tools/ISuperProvider.java b/src/main/java/libKonogonka/Tools/ISuperProvider.java index 27b3822..b6b46a6 100644 --- a/src/main/java/libKonogonka/Tools/ISuperProvider.java +++ b/src/main/java/libKonogonka/Tools/ISuperProvider.java @@ -18,7 +18,7 @@ */ package libKonogonka.Tools; -import libKonogonka.ctraes.InFileStreamProducer; +import libKonogonka.aesctr.InFileStreamProducer; import java.io.File; diff --git a/src/main/java/libKonogonka/Tools/NCA/NCAContent.java b/src/main/java/libKonogonka/Tools/NCA/NCAContent.java index 4052b35..9451d5b 100644 --- a/src/main/java/libKonogonka/Tools/NCA/NCAContent.java +++ b/src/main/java/libKonogonka/Tools/NCA/NCAContent.java @@ -21,9 +21,9 @@ package libKonogonka.Tools.NCA; import libKonogonka.Tools.NCA.NCASectionTableBlock.NcaFsHeader; import libKonogonka.Tools.PFS0.PFS0Provider; import libKonogonka.Tools.RomFs.RomFsProvider; -import libKonogonka.ctraes.AesCtrBufferedInputStream; -import libKonogonka.ctraes.AesCtrDecryptForMediaBlocks; -import libKonogonka.ctraes.InFileStreamProducer; +import libKonogonka.aesctr.AesCtrBufferedInputStream; +import libKonogonka.aesctr.AesCtrDecryptForMediaBlocks; +import libKonogonka.aesctr.InFileStreamProducer; import libKonogonka.exceptions.EmptySectionException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/src/main/java/libKonogonka/Tools/NCA/NCASectionTableBlock/NcaFsHeader.java b/src/main/java/libKonogonka/Tools/NCA/NCASectionTableBlock/NcaFsHeader.java index 3419d66..dda045e 100644 --- a/src/main/java/libKonogonka/Tools/NCA/NCASectionTableBlock/NcaFsHeader.java +++ b/src/main/java/libKonogonka/Tools/NCA/NCASectionTableBlock/NcaFsHeader.java @@ -18,6 +18,7 @@ */ package libKonogonka.Tools.NCA.NCASectionTableBlock; +import libKonogonka.Converter; import libKonogonka.RainbowDump; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -82,7 +83,7 @@ public class NcaFsHeader { generation = Arrays.copyOfRange(tableBlockBytes, 0x140, 0x144); secureValue = Arrays.copyOfRange(tableBlockBytes, 0x144, 0x148); - sectionCTR = Arrays.copyOfRange(tableBlockBytes, 0x140, 0x148); + sectionCTR = Converter.flip(Arrays.copyOfRange(tableBlockBytes, 0x140, 0x148)); sparseInfo = new SparseInfo(Arrays.copyOfRange(tableBlockBytes, 0x148, 0x178)); compressionInfo = new CompressionInfo(Arrays.copyOfRange(tableBlockBytes, 0x178, 0x1a0)); diff --git a/src/main/java/libKonogonka/Tools/NPDM/NPDMProvider.java b/src/main/java/libKonogonka/Tools/NPDM/NPDMProvider.java index b930c74..6fe4d88 100644 --- a/src/main/java/libKonogonka/Tools/NPDM/NPDMProvider.java +++ b/src/main/java/libKonogonka/Tools/NPDM/NPDMProvider.java @@ -20,7 +20,7 @@ package libKonogonka.Tools.NPDM; import libKonogonka.Tools.NPDM.ACI0.ACI0Provider; import libKonogonka.Tools.NPDM.ACID.ACIDProvider; -import libKonogonka.ctraes.InFileStreamProducer; +import libKonogonka.aesctr.InFileStreamProducer; import java.io.BufferedInputStream; import java.io.File; diff --git a/src/main/java/libKonogonka/Tools/NSO/NSO0Provider.java b/src/main/java/libKonogonka/Tools/NSO/NSO0Provider.java index cbb9567..adb5794 100644 --- a/src/main/java/libKonogonka/Tools/NSO/NSO0Provider.java +++ b/src/main/java/libKonogonka/Tools/NSO/NSO0Provider.java @@ -18,7 +18,7 @@ */ package libKonogonka.Tools.NSO; -import libKonogonka.ctraes.InFileStreamProducer; +import libKonogonka.aesctr.InFileStreamProducer; import java.io.BufferedInputStream; import java.io.File; diff --git a/src/main/java/libKonogonka/Tools/NSO/NSO0Unpacker.java b/src/main/java/libKonogonka/Tools/NSO/NSO0Unpacker.java index a26b625..8e47b0b 100644 --- a/src/main/java/libKonogonka/Tools/NSO/NSO0Unpacker.java +++ b/src/main/java/libKonogonka/Tools/NSO/NSO0Unpacker.java @@ -18,7 +18,7 @@ */ package libKonogonka.Tools.NSO; -import libKonogonka.ctraes.InFileStreamProducer; +import libKonogonka.aesctr.InFileStreamProducer; import net.jpountz.lz4.LZ4Factory; import net.jpountz.lz4.LZ4SafeDecompressor; diff --git a/src/main/java/libKonogonka/Tools/PFS0/PFS0Provider.java b/src/main/java/libKonogonka/Tools/PFS0/PFS0Provider.java index 60d3a6f..461439f 100644 --- a/src/main/java/libKonogonka/Tools/PFS0/PFS0Provider.java +++ b/src/main/java/libKonogonka/Tools/PFS0/PFS0Provider.java @@ -22,13 +22,11 @@ import libKonogonka.RainbowDump; import libKonogonka.Tools.ExportAble; import libKonogonka.Tools.ISuperProvider; import libKonogonka.Tools.NCA.NCASectionTableBlock.SuperBlockPFS0; -import libKonogonka.ctraes.InFileStreamProducer; +import libKonogonka.aesctr.InFileStreamProducer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.*; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.LinkedList; public class PFS0Provider extends ExportAble implements ISuperProvider { diff --git a/src/main/java/libKonogonka/Tools/RomFs/RomFsConstruct.java b/src/main/java/libKonogonka/Tools/RomFs/RomFsConstruct.java index 2b9da2f..98bfb79 100644 --- a/src/main/java/libKonogonka/Tools/RomFs/RomFsConstruct.java +++ b/src/main/java/libKonogonka/Tools/RomFs/RomFsConstruct.java @@ -19,7 +19,7 @@ package libKonogonka.Tools.RomFs; import libKonogonka.Converter; -import libKonogonka.ctraes.InFileStreamProducer; +import libKonogonka.aesctr.InFileStreamProducer; import java.io.BufferedInputStream; diff --git a/src/main/java/libKonogonka/Tools/RomFs/RomFsProvider.java b/src/main/java/libKonogonka/Tools/RomFs/RomFsProvider.java index b90fefd..10062f9 100644 --- a/src/main/java/libKonogonka/Tools/RomFs/RomFsProvider.java +++ b/src/main/java/libKonogonka/Tools/RomFs/RomFsProvider.java @@ -21,7 +21,7 @@ package libKonogonka.Tools.RomFs; import libKonogonka.Tools.ExportAble; import libKonogonka.Tools.RomFs.view.DirectoryMetaTablePlainView; import libKonogonka.Tools.RomFs.view.FileMetaTablePlainView; -import libKonogonka.ctraes.InFileStreamProducer; +import libKonogonka.aesctr.InFileStreamProducer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/src/main/java/libKonogonka/Tools/XCI/HFS0Provider.java b/src/main/java/libKonogonka/Tools/XCI/HFS0Provider.java index 26ca5ee..2cc6000 100644 --- a/src/main/java/libKonogonka/Tools/XCI/HFS0Provider.java +++ b/src/main/java/libKonogonka/Tools/XCI/HFS0Provider.java @@ -20,14 +20,12 @@ package libKonogonka.Tools.XCI; import libKonogonka.Tools.ExportAble; import libKonogonka.Tools.ISuperProvider; -import libKonogonka.ctraes.InFileStreamProducer; +import libKonogonka.aesctr.InFileStreamProducer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.*; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.Arrays; import static libKonogonka.Converter.*; diff --git a/src/main/java/libKonogonka/Tools/other/System2/System2Header.java b/src/main/java/libKonogonka/Tools/other/System2/System2Header.java index 817913e..a6be53b 100644 --- a/src/main/java/libKonogonka/Tools/other/System2/System2Header.java +++ b/src/main/java/libKonogonka/Tools/other/System2/System2Header.java @@ -20,7 +20,7 @@ package libKonogonka.Tools.other.System2; import libKonogonka.Converter; import libKonogonka.RainbowDump; -import libKonogonka.ctraesclassic.AesCtrDecryptClassic; +import libKonogonka.aesctr.AesCtrDecryptClassic; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/src/main/java/libKonogonka/Tools/other/System2/System2Provider.java b/src/main/java/libKonogonka/Tools/other/System2/System2Provider.java index 58a1bcb..71f3343 100644 --- a/src/main/java/libKonogonka/Tools/other/System2/System2Provider.java +++ b/src/main/java/libKonogonka/Tools/other/System2/System2Provider.java @@ -21,8 +21,8 @@ package libKonogonka.Tools.other.System2; import libKonogonka.KeyChainHolder; import libKonogonka.Tools.ExportAble; import libKonogonka.Tools.other.System2.ini1.Ini1Provider; -import libKonogonka.ctraes.InFileStreamProducer; -import libKonogonka.ctraesclassic.InFileStreamClassicProducer; +import libKonogonka.aesctr.InFileStreamProducer; +import libKonogonka.aesctr.InFileStreamClassicProducer; import java.io.*; import java.nio.ByteBuffer; diff --git a/src/main/java/libKonogonka/Tools/other/System2/ini1/Ini1Provider.java b/src/main/java/libKonogonka/Tools/other/System2/ini1/Ini1Provider.java index 7994eac..ac94341 100644 --- a/src/main/java/libKonogonka/Tools/other/System2/ini1/Ini1Provider.java +++ b/src/main/java/libKonogonka/Tools/other/System2/ini1/Ini1Provider.java @@ -20,7 +20,7 @@ package libKonogonka.Tools.other.System2.ini1; import libKonogonka.Tools.ExportAble; import libKonogonka.Tools.other.System2.System2Header; -import libKonogonka.ctraesclassic.InFileStreamClassicProducer; +import libKonogonka.aesctr.InFileStreamClassicProducer; import java.io.IOException; import java.nio.file.Files; diff --git a/src/main/java/libKonogonka/Tools/other/System2/ini1/KIP1Provider.java b/src/main/java/libKonogonka/Tools/other/System2/ini1/KIP1Provider.java index 15dc1bb..79b2b75 100644 --- a/src/main/java/libKonogonka/Tools/other/System2/ini1/KIP1Provider.java +++ b/src/main/java/libKonogonka/Tools/other/System2/ini1/KIP1Provider.java @@ -19,7 +19,7 @@ package libKonogonka.Tools.other.System2.ini1; import libKonogonka.Tools.ExportAble; -import libKonogonka.ctraesclassic.InFileStreamClassicProducer; +import libKonogonka.aesctr.InFileStreamClassicProducer; import java.nio.file.Paths; diff --git a/src/main/java/libKonogonka/Tools/other/System2/ini1/Kip1Unpacker.java b/src/main/java/libKonogonka/Tools/other/System2/ini1/Kip1Unpacker.java index f6287c9..a08cdec 100644 --- a/src/main/java/libKonogonka/Tools/other/System2/ini1/Kip1Unpacker.java +++ b/src/main/java/libKonogonka/Tools/other/System2/ini1/Kip1Unpacker.java @@ -20,7 +20,7 @@ package libKonogonka.Tools.other.System2.ini1; import libKonogonka.Tools.NSO.SegmentHeader; import libKonogonka.blz.BlzDecompress; -import libKonogonka.ctraesclassic.InFileStreamClassicProducer; +import libKonogonka.aesctr.InFileStreamClassicProducer; import java.io.BufferedInputStream; import java.io.File; diff --git a/src/main/java/libKonogonka/ctraes/AesCtrBufferedInputStream.java b/src/main/java/libKonogonka/aesctr/AesCtrBufferedInputStream.java similarity index 77% rename from src/main/java/libKonogonka/ctraes/AesCtrBufferedInputStream.java rename to src/main/java/libKonogonka/aesctr/AesCtrBufferedInputStream.java index dd8d759..db2824b 100644 --- a/src/main/java/libKonogonka/ctraes/AesCtrBufferedInputStream.java +++ b/src/main/java/libKonogonka/aesctr/AesCtrBufferedInputStream.java @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with libKonogonka. If not, see . */ -package libKonogonka.ctraes; +package libKonogonka.aesctr; import libKonogonka.RainbowDump; import org.apache.logging.log4j.LogManager; @@ -27,15 +27,24 @@ import java.io.*; public class AesCtrBufferedInputStream extends BufferedInputStream { private final static Logger log = LogManager.getLogger(AesCtrBufferedInputStream.class); - private final AesCtrDecryptForMediaBlocks decryptor; - private final long mediaOffsetPositionStart; - private final long mediaOffsetPositionEnd; + private final AesCtrDecrypt decryptor; + private final long encryptedStartOffset; + private final long encryptedEndOffset; private final long fileSize; private byte[] decryptedBytes; private long pseudoPos; private int pointerInsideDecryptedSection; + /** + * AES CTR for 'Media Blocks'. Used in NCA. + * @param decryptor AesCtrDecryptForMediaBlocks + * @param ncaOffsetPosition NCA offset in file. If NCA is inside XCI, NSP. Otherwise, must be 0. + * @param mediaStartOffset 'Media Start Offset' in NCA representation. Small value, not bytes. + * @param mediaEndOffset 'Media End Offset' in NCA representation. Small value, not bytes. + * @param inputStream InputStream as it used in regular BufferedInputStream. + * @param fileSize File size or InputStream size. + */ public AesCtrBufferedInputStream(AesCtrDecryptForMediaBlocks decryptor, long ncaOffsetPosition, long mediaStartOffset, @@ -44,13 +53,36 @@ public class AesCtrBufferedInputStream extends BufferedInputStream { long fileSize){ super(inputStream, 0x200); this.decryptor = decryptor; - this.mediaOffsetPositionStart = ncaOffsetPosition + (mediaStartOffset * 0x200); - this.mediaOffsetPositionEnd = ncaOffsetPosition + (mediaEndOffset * 0x200); + this.encryptedStartOffset = ncaOffsetPosition + (mediaStartOffset * 0x200); + this.encryptedEndOffset = ncaOffsetPosition + (mediaEndOffset * 0x200); this.fileSize = fileSize; log.trace("\n Offset Position "+ncaOffsetPosition+ - "\n MediaOffsetPositionStart "+RainbowDump.formatDecHexString(mediaOffsetPositionStart)+ - "\n MediaOffsetPositionEnd "+RainbowDump.formatDecHexString(mediaOffsetPositionEnd)); + "\n MediaOffsetPositionStart "+RainbowDump.formatDecHexString(encryptedStartOffset)+ + "\n MediaOffsetPositionEnd "+RainbowDump.formatDecHexString(encryptedEndOffset)); + } + + /** + * AES CTR 'classic' implementation. Used for system2 (PK21) decrypt. + * @param decryptor AesCtrDecryptClassic + * @param encryptedStartOffset Encrypted start position in bytes. + * @param encryptedEndOffset Encrypted start position in bytes. + * @param inputStream InputStream as it used in regular BufferedInputStream + * @param fileSize File size or InputStream size. + */ + public AesCtrBufferedInputStream(AesCtrDecryptClassic decryptor, + long encryptedStartOffset, + long encryptedEndOffset, + InputStream inputStream, + long fileSize){ + super(inputStream, 0x200); + this.decryptor = decryptor; + this.encryptedStartOffset = encryptedStartOffset; + this.encryptedEndOffset = encryptedEndOffset; + this.fileSize = fileSize; + + log.trace(" EncryptedStartOffset : "+RainbowDump.formatDecHexString(encryptedStartOffset)+ + "\n EncryptedEndOffset : "+RainbowDump.formatDecHexString(encryptedEndOffset)); } @Override @@ -81,7 +113,7 @@ public class AesCtrBufferedInputStream extends BufferedInputStream { System.arraycopy(decryptedBytes, 0, b, bytesFromFirstBlock+i*0x200, 0x200); } //3 - if(fileSize > (pseudoPos+ len)) { + if(fileSize > (pseudoPos+len)) { fillDecryptedCache(); System.arraycopy(decryptedBytes, 0, b, bytesFromFirstBlock + middleBlocksCount * 0x200, bytesFromLastBlock); } @@ -90,7 +122,7 @@ public class AesCtrBufferedInputStream extends BufferedInputStream { return b.length; } log.trace("1. Pointer Inside + End Position Outside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+b.length)+")"); - int middleBlocksCount = (int) ((mediaOffsetPositionEnd - (pseudoPos+bytesFromFirstBlock)) / 0x200); + int middleBlocksCount = (int) ((encryptedEndOffset - (pseudoPos+bytesFromFirstBlock)) / 0x200); int bytesFromEnd = len - bytesFromFirstBlock - middleBlocksCount * 0x200; //1 System.arraycopy(decryptedBytes, pointerInsideDecryptedSection, b, 0, bytesFromFirstBlock); @@ -105,10 +137,10 @@ public class AesCtrBufferedInputStream extends BufferedInputStream { pseudoPos += len; pointerInsideDecryptedSection = 0; return b.length; - } + } if (isEndPositionInsideEncryptedSection(len)) { log.trace("2. End Position Inside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+b.length)+")"); - int bytesTillEncrypted = (int) (mediaOffsetPositionStart - pseudoPos); + int bytesTillEncrypted = (int) (encryptedStartOffset - pseudoPos); int fullEncryptedBlocks = (len - bytesTillEncrypted) / 0x200; int incompleteEncryptedBytes = (len - bytesTillEncrypted) % 0x200; System.arraycopy(readChunk(bytesTillEncrypted), 0, b, 0, bytesTillEncrypted); @@ -133,34 +165,31 @@ public class AesCtrBufferedInputStream extends BufferedInputStream { try{ decryptedBytes = decryptor.decryptNext(readChunk(0x200)); } - catch (Exception e){ - throw new IOException(e); - } + catch (Exception e){ throw new IOException(e); } } + private void resetAndSkip(long blockSum) throws IOException{ try{ - decryptor.reset(); - decryptor.skipNext(blockSum); // recalculate - } - catch (Exception e){ - throw new IOException(e); + decryptor.resetAndSkip(blockSum); } + catch (Exception e){ throw new IOException(e); } } + private byte[] readChunk(int bytes) throws IOException{ byte[] chunkBytes = new byte[bytes]; long actuallyRead = super.read(chunkBytes, 0, bytes); if (actuallyRead != bytes) - throw new IOException("Can't read. " + actuallyRead + "/"+ bytes); + throw new IOException("Can't read. "+ bytes +"/" + actuallyRead); return chunkBytes; } private boolean isPointerInsideEncryptedSection(){ - return (pseudoPos-pointerInsideDecryptedSection >= mediaOffsetPositionStart) && - (pseudoPos-pointerInsideDecryptedSection < mediaOffsetPositionEnd); + return (pseudoPos-pointerInsideDecryptedSection >= encryptedStartOffset) && + (pseudoPos-pointerInsideDecryptedSection < encryptedEndOffset); } private boolean isEndPositionInsideEncryptedSection(long requestedBytesCount){ - return ((pseudoPos-pointerInsideDecryptedSection + requestedBytesCount) >= mediaOffsetPositionStart) && - ((pseudoPos-pointerInsideDecryptedSection + requestedBytesCount) < mediaOffsetPositionEnd); + return ((pseudoPos-pointerInsideDecryptedSection + requestedBytesCount) >= encryptedStartOffset) && + ((pseudoPos-pointerInsideDecryptedSection + requestedBytesCount) < encryptedEndOffset); } @Override @@ -175,7 +204,7 @@ public class AesCtrBufferedInputStream extends BufferedInputStream { if (isEndPositionInsideEncryptedSection(n)){ // If we need to move somewhere out of the encrypted section log.trace("4.1. Pointer Inside + End Position Inside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+n)+")"); - long blocksToSkipCountingFromStart = (pseudoPos+n - mediaOffsetPositionStart) / 0x200; // always positive + long blocksToSkipCountingFromStart = (pseudoPos+n - encryptedStartOffset) / 0x200; // always positive resetAndSkip(blocksToSkipCountingFromStart); long leftovers = realCountOfBytesToSkip % 0x200; // most likely will be 0; TODO: a lot of tests @@ -197,7 +226,7 @@ public class AesCtrBufferedInputStream extends BufferedInputStream { if (isEndPositionInsideEncryptedSection(n)) { //pointer will be inside Encrypted Section, but now outside log.trace("5. End Position Inside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+n)+")"); //skip to start if the block we need - long bytesToSkipTillEncryptedBlock = mediaOffsetPositionStart - pseudoPos; + long bytesToSkipTillEncryptedBlock = encryptedStartOffset - pseudoPos; long blocksToSkipCountingFromStart = (n - bytesToSkipTillEncryptedBlock) / 0x200; // always positive long bytesToSkipTillRequiredBlock = bytesToSkipTillEncryptedBlock + blocksToSkipCountingFromStart * 0x200; long leftovers = n - bytesToSkipTillRequiredBlock; // most likely will be 0; @@ -230,6 +259,7 @@ public class AesCtrBufferedInputStream extends BufferedInputStream { log.trace("Skip loop: skipped: "+skipped+"\tmustSkip "+mustSkip); } } + @Override public synchronized int read() throws IOException { byte[] b = new byte[1]; diff --git a/src/main/java/libKonogonka/aesctr/AesCtrDecrypt.java b/src/main/java/libKonogonka/aesctr/AesCtrDecrypt.java new file mode 100644 index 0000000..78f86b9 --- /dev/null +++ b/src/main/java/libKonogonka/aesctr/AesCtrDecrypt.java @@ -0,0 +1,32 @@ +package libKonogonka.aesctr; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import java.security.Security; + +public abstract class AesCtrDecrypt { + private static boolean shouldBeInitialized = true; + + protected AesCtrDecrypt(){ + if (shouldBeInitialized){ + Security.addProvider(new BouncyCastleProvider()); + shouldBeInitialized = false; + } + } + + /** + * Decrypts next block of bytes. Usually 0x200. + * @param encryptedBlock Encrypted bytes + * @return Decrypted bytes + */ + abstract public byte[] decryptNext(byte[] encryptedBlock); + /** + * Initializes cipher again using updated IV (CTR) + * @param blockCount - how many blockCount from encrypted section start should be skipped. Block size = 0x200 + * */ + abstract public void resetAndSkip(long blockCount) throws Exception; + /** + * Initializes cipher again using initial IV (CTR) + * */ + abstract public void reset() throws Exception; +} diff --git a/src/main/java/libKonogonka/ctraesclassic/AesCtrDecryptClassic.java b/src/main/java/libKonogonka/aesctr/AesCtrDecryptClassic.java similarity index 60% rename from src/main/java/libKonogonka/ctraesclassic/AesCtrDecryptClassic.java rename to src/main/java/libKonogonka/aesctr/AesCtrDecryptClassic.java index d0d9372..d8ebeca 100644 --- a/src/main/java/libKonogonka/ctraesclassic/AesCtrDecryptClassic.java +++ b/src/main/java/libKonogonka/aesctr/AesCtrDecryptClassic.java @@ -16,51 +16,35 @@ You should have received a copy of the GNU General Public License along with libKonogonka. If not, see . */ -package libKonogonka.ctraesclassic; +package libKonogonka.aesctr; -import org.bouncycastle.jce.provider.BouncyCastleProvider; +import libKonogonka.Converter; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.math.BigInteger; import java.nio.ByteBuffer; -import java.security.Security; - -public class AesCtrDecryptClassic { - - private static boolean BCinitialized = false; - - private void initBCProvider(){ - Security.addProvider(new BouncyCastleProvider()); - BCinitialized = true; - } +public class AesCtrDecryptClassic extends AesCtrDecrypt { private final SecretKeySpec key; private final byte[] ivArray; private Cipher cipher; public AesCtrDecryptClassic(String keyString, byte[] ivArray) throws Exception{ - if ( ! BCinitialized) - initBCProvider(); - byte[] keyArray = hexStrToByteArray(keyString); + super(); + this.key = new SecretKeySpec(Converter.hexStringToByteArray(keyString), "AES"); this.ivArray = ivArray; - key = new SecretKeySpec(keyArray, "AES"); - cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC"); - IvParameterSpec iv = new IvParameterSpec(ivArray.clone()); - cipher.init(Cipher.DECRYPT_MODE, key, iv); + reset(); } - + @Override public byte[] decryptNext(byte[] encryptedData) { return cipher.update(encryptedData); } - /** - * Initializes cipher again using updated IV - * @param blocks - how many blocks from encrypted section start should be skipped. Block size = 0x200 - * */ - public void resetAndSkip(long blocks) throws Exception{ - reset(calculateCtr(blocks * 0x200)); + @Override + public void resetAndSkip(long blockCount) throws Exception{ + reset(calculateCtr(blockCount * 0x200)); } private byte[] calculateCtr(long offset){ BigInteger ctr = new BigInteger(ivArray); @@ -75,9 +59,7 @@ public class AesCtrDecryptClassic { return ctrCalculated; } - /** - * Initializes cipher again using initial IV - * */ + @Override public void reset() throws Exception{ reset(ivArray.clone()); } @@ -87,14 +69,4 @@ public class AesCtrDecryptClassic { IvParameterSpec iv = new IvParameterSpec(updatedIvArray); cipher.init(Cipher.DECRYPT_MODE, key, iv); } - - private byte[] hexStrToByteArray(String s) { - int len = s.length(); - byte[] data = new byte[len / 2]; - for (int i = 0; i < len; i += 2) { - data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) - + Character.digit(s.charAt(i+1), 16)); - } - return data; - } } diff --git a/src/main/java/libKonogonka/aesctr/AesCtrDecryptForMediaBlocks.java b/src/main/java/libKonogonka/aesctr/AesCtrDecryptForMediaBlocks.java new file mode 100644 index 0000000..4eac17e --- /dev/null +++ b/src/main/java/libKonogonka/aesctr/AesCtrDecryptForMediaBlocks.java @@ -0,0 +1,66 @@ +/* + Copyright 2019-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 . +*/ +package libKonogonka.aesctr; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.util.Arrays; + +/** + * Simplify decryption for NCA's AES CTR sections + */ +public class AesCtrDecryptForMediaBlocks extends AesCtrDecrypt { + private final SecretKeySpec key; + private final byte[] ivArray; + private Cipher cipher; + + private final long initialOffset; + + public AesCtrDecryptForMediaBlocks(byte[] key, byte[] sectionCTR, long realMediaOffset) throws Exception{ + super(); + this.key = new SecretKeySpec(key, "AES"); + this.ivArray = Arrays.copyOf(sectionCTR, 0x10); // IV for CTR == 16 bytes; Populate first 4 bytes taken from Header's section Block CTR (aka SecureValue) + this.initialOffset = realMediaOffset; + reset(); + } + @Override + public byte[] decryptNext(byte[] encryptedBlock){ + return cipher.update(encryptedBlock); + } + @Override + public void resetAndSkip(long blockCount) throws Exception{ + cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC"); + long mediaOffset = initialOffset + (blockCount * 0x200L); + cipher.init(Cipher.DECRYPT_MODE, key, getIv(mediaOffset)); + } + private IvParameterSpec getIv(long mediaOffset){ // Populate last 8 bytes calculated. Thanks hactool! + byte[] iv = ivArray.clone(); + long offset = mediaOffset >> 4; + for (int i = 0; i < 8; i++){ + iv[0x10-i-1] = (byte)(offset & 0xff); + offset >>= 8; + } + return new IvParameterSpec(iv); + } + @Override + public void reset() throws Exception{ + resetAndSkip(0); + } +} diff --git a/src/main/java/libKonogonka/ctraesclassic/InFileStreamClassicProducer.java b/src/main/java/libKonogonka/aesctr/InFileStreamClassicProducer.java similarity index 97% rename from src/main/java/libKonogonka/ctraesclassic/InFileStreamClassicProducer.java rename to src/main/java/libKonogonka/aesctr/InFileStreamClassicProducer.java index dbe1700..5eec1ff 100644 --- a/src/main/java/libKonogonka/ctraesclassic/InFileStreamClassicProducer.java +++ b/src/main/java/libKonogonka/aesctr/InFileStreamClassicProducer.java @@ -16,10 +16,9 @@ You should have received a copy of the GNU General Public License along with libKonogonka. If not, see . */ -package libKonogonka.ctraesclassic; +package libKonogonka.aesctr; import libKonogonka.IProducer; -import libKonogonka.ctraes.InFileStreamProducer; import java.io.BufferedInputStream; import java.io.File; @@ -145,7 +144,7 @@ public class InFileStreamClassicProducer implements IProducer { else is = Files.newInputStream(filePath); - AesCtrClassicBufferedInputStream stream = new AesCtrClassicBufferedInputStream( + AesCtrBufferedInputStream stream = new AesCtrBufferedInputStream( decryptor, encryptedStartOffset, encryptedEndOffset, is, fileSize); if (offset != stream.skip(offset)) diff --git a/src/main/java/libKonogonka/ctraes/InFileStreamProducer.java b/src/main/java/libKonogonka/aesctr/InFileStreamProducer.java similarity index 99% rename from src/main/java/libKonogonka/ctraes/InFileStreamProducer.java rename to src/main/java/libKonogonka/aesctr/InFileStreamProducer.java index a45a03e..c52ddd1 100644 --- a/src/main/java/libKonogonka/ctraes/InFileStreamProducer.java +++ b/src/main/java/libKonogonka/aesctr/InFileStreamProducer.java @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with libKonogonka. If not, see . */ -package libKonogonka.ctraes; +package libKonogonka.aesctr; import libKonogonka.IProducer; diff --git a/src/main/java/libKonogonka/ctraes/AesCtrDecryptForMediaBlocks.java b/src/main/java/libKonogonka/ctraes/AesCtrDecryptForMediaBlocks.java deleted file mode 100644 index 46fe3ec..0000000 --- a/src/main/java/libKonogonka/ctraes/AesCtrDecryptForMediaBlocks.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - Copyright 2019-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 . -*/ -package libKonogonka.ctraes; - -import libKonogonka.Converter; -import org.bouncycastle.jce.provider.BouncyCastleProvider; - -import javax.crypto.Cipher; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; -import java.security.Security; - -/** - * Simplify decryption of the CTR for NCA's AesCtr sections - */ -public class AesCtrDecryptForMediaBlocks { - - private static boolean BCinitialized = false; - private Cipher cipher; - private final SecretKeySpec key; - - private long realMediaOffset; - private byte[] ivArray; - - private final byte[] initialSectionCTR; - private final long initialRealMediaOffset; - - public AesCtrDecryptForMediaBlocks(byte[] key, byte[] sectionCTR, long realMediaOffset) throws Exception{ - if ( ! BCinitialized) - initBCProvider(); - this.key = new SecretKeySpec(key, "AES"); - this.initialSectionCTR = sectionCTR; - this.initialRealMediaOffset = realMediaOffset; - reset(); - } - private void initBCProvider(){ - Security.addProvider(new BouncyCastleProvider()); - BCinitialized = true; - } - - public void skipNext(){ - realMediaOffset += 0x200; - } - - public void skipNext(long blocksNum){ - realMediaOffset += blocksNum * 0x200; - } - - public byte[] decryptNext(byte[] encryptedBlock) throws Exception{ - updateIV(); - byte[] decryptedBlock = decrypt(encryptedBlock); - realMediaOffset += 0x200; - return decryptedBlock; - } - // Populate last 8 bytes calculated. Thanks hactool project! - private void updateIV(){ - long offset = realMediaOffset >> 4; - for (int i = 0; i < 0x8; i++){ - ivArray[0x10-i-1] = (byte)(offset & 0xff); // Note: issues could be here - offset >>= 8; - } - } - private byte[] decrypt(byte[] encryptedData) throws Exception{ - IvParameterSpec iv = new IvParameterSpec(ivArray); - cipher.init(Cipher.DECRYPT_MODE, key, iv); - return cipher.doFinal(encryptedData); - } - - public void reset() throws Exception{ - realMediaOffset = initialRealMediaOffset; - cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC"); - // IV for CTR == 16 bytes - ivArray = new byte[0x10]; - // Populate first 4 bytes taken from Header's section Block CTR (aka SecureValue) - System.arraycopy(Converter.flip(initialSectionCTR), 0x0, ivArray, 0x0, 0x8); - } -} diff --git a/src/main/java/libKonogonka/ctraesclassic/AesCtrClassicBufferedInputStream.java b/src/main/java/libKonogonka/ctraesclassic/AesCtrClassicBufferedInputStream.java deleted file mode 100644 index c0a9293..0000000 --- a/src/main/java/libKonogonka/ctraesclassic/AesCtrClassicBufferedInputStream.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - Copyright 2019-2023 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 . - */ -package libKonogonka.ctraesclassic; - -import libKonogonka.RainbowDump; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.*; - -public class AesCtrClassicBufferedInputStream extends BufferedInputStream { - private final static Logger log = LogManager.getLogger(AesCtrClassicBufferedInputStream.class); - - private final AesCtrDecryptClassic decryptor; - private final long encryptedStartOffset; - private final long encryptedEndOffset; - private final long fileSize; - - private byte[] decryptedBytes; - private long pseudoPos; - private int pointerInsideDecryptedSection; - - public AesCtrClassicBufferedInputStream(AesCtrDecryptClassic decryptor, - long encryptedStartOffset, - long encryptedEndOffset, - InputStream inputStream, - long fileSize){ - super(inputStream, 0x200); - this.decryptor = decryptor; - this.encryptedStartOffset = encryptedStartOffset; - this.encryptedEndOffset = encryptedEndOffset; - this.fileSize = fileSize; - - log.trace(" EncryptedStartOffset : "+RainbowDump.formatDecHexString(encryptedStartOffset)+ - "\n EncryptedEndOffset : "+RainbowDump.formatDecHexString(encryptedEndOffset)); - } - - @Override - public synchronized int read(byte[] b) throws IOException{ - int bytesToRead = b.length; - if (isPointerInsideEncryptedSection()){ - int bytesFromFirstBlock = 0x200 - pointerInsideDecryptedSection; - if (bytesFromFirstBlock > bytesToRead){ - log.trace("1.2. Pointer Inside + End Position Inside (Decrypted) Encrypted Section ("+pseudoPos+"-"+(pseudoPos+b.length)+")"); - System.arraycopy(decryptedBytes, pointerInsideDecryptedSection, b, 0, bytesToRead); - - pseudoPos += bytesToRead; - pointerInsideDecryptedSection += bytesToRead; - return b.length; - } - - if (isEndPositionInsideEncryptedSection(b.length)) { - log.trace("1.1. Pointer Inside + End Position Inside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+b.length)+")"); - int middleBlocksCount = (bytesToRead - bytesFromFirstBlock) / 0x200; - int bytesFromLastBlock = (bytesToRead - bytesFromFirstBlock) % 0x200; - //1 - System.arraycopy(decryptedBytes, pointerInsideDecryptedSection, b, 0, bytesFromFirstBlock); - //2 - for (int i = 0; i < middleBlocksCount; i++) { - fillDecryptedCache(); - System.arraycopy(decryptedBytes, 0, b, bytesFromFirstBlock+i*0x200, 0x200); - } - //3 - if(fileSize > (pseudoPos+bytesToRead)) { - fillDecryptedCache(); - System.arraycopy(decryptedBytes, 0, b, bytesFromFirstBlock + middleBlocksCount * 0x200, bytesFromLastBlock); - } - pseudoPos += bytesToRead; - pointerInsideDecryptedSection = bytesFromLastBlock; - return b.length; - } - log.trace("1. Pointer Inside + End Position Outside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+b.length)+")"); - int middleBlocksCount = (int) ((encryptedEndOffset - (pseudoPos+bytesFromFirstBlock)) / 0x200); - int bytesFromEnd = bytesToRead - bytesFromFirstBlock - middleBlocksCount * 0x200; - //1 - System.arraycopy(decryptedBytes, pointerInsideDecryptedSection, b, 0, bytesFromFirstBlock); - //2 - //log.debug("\n"+bytesFromFirstBlock+"\n"+ middleBlocksCount+" = "+(middleBlocksCount*0x200)+" bytes\n"+ bytesFromEnd+"\n"); - for (int i = 0; i < middleBlocksCount; i++) { - fillDecryptedCache(); - System.arraycopy(decryptedBytes, 0, b, bytesFromFirstBlock+i*0x200, 0x200); - } - //3 // TODO: if it's zero? - System.arraycopy(readChunk(bytesFromEnd), 0, b, bytesFromFirstBlock+middleBlocksCount*0x200, bytesFromEnd); - pseudoPos += bytesToRead; - pointerInsideDecryptedSection = 0; - return b.length; - } - if (isEndPositionInsideEncryptedSection(bytesToRead)) { - log.trace("2. End Position Inside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+b.length)+")"); - int bytesTillEncrypted = (int) (encryptedStartOffset - pseudoPos); - int fullEncryptedBlocks = (bytesToRead - bytesTillEncrypted) / 0x200; - int incompleteEncryptedBytes = (bytesToRead - bytesTillEncrypted) % 0x200; - System.arraycopy(readChunk(bytesTillEncrypted), 0, b, 0, bytesTillEncrypted); - //2 - for (int i = 0; i < fullEncryptedBlocks; i++) { - fillDecryptedCache(); - System.arraycopy(decryptedBytes, 0, b, fullEncryptedBlocks+i*0x200, 0x200); - } - //3 - fillDecryptedCache(); - System.arraycopy(decryptedBytes, 0, b, bytesTillEncrypted+fullEncryptedBlocks*0x200, incompleteEncryptedBytes); - pseudoPos += bytesToRead; - pointerInsideDecryptedSection = incompleteEncryptedBytes; - return b.length; - } - log.trace("3. Not encrypted ("+pseudoPos+"-"+(pseudoPos+b.length)+")"); - pseudoPos += bytesToRead; - pointerInsideDecryptedSection = 0; - return super.read(b); - } - private void fillDecryptedCache() throws IOException{ - try{ - decryptedBytes = decryptor.decryptNext(readChunk(0x200)); - } - catch (Exception e){ - throw new IOException(e); - } - } - - private byte[] readChunk(int bytes) throws IOException{ - byte[] chunkBytes = new byte[bytes]; - long actuallyRead = super.read(chunkBytes); - if (actuallyRead != bytes) - throw new IOException("Can't read. "+ bytes +"/" + actuallyRead); - return chunkBytes; - } - - private boolean isPointerInsideEncryptedSection(){ - return (pseudoPos-pointerInsideDecryptedSection >= encryptedStartOffset) && - (pseudoPos-pointerInsideDecryptedSection < encryptedEndOffset); - } - private boolean isEndPositionInsideEncryptedSection(long requestedBytesCount){ - return ((pseudoPos-pointerInsideDecryptedSection + requestedBytesCount) >= encryptedStartOffset) && - ((pseudoPos-pointerInsideDecryptedSection + requestedBytesCount) < encryptedEndOffset); - } - - @Override - public synchronized long skip(long n) throws IOException { - if (isPointerInsideEncryptedSection()){ - long realCountOfBytesToSkip = n - (0x200 - pointerInsideDecryptedSection); - if (realCountOfBytesToSkip <= 0){ - pseudoPos += n; - pointerInsideDecryptedSection += n; - return n; - } - - if (isEndPositionInsideEncryptedSection(n)){ // If we need to move somewhere out of the encrypted section - log.trace("4.1. Pointer Inside + End Position Inside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+n)+")"); - long blocksToSkipCountingFromStart = (pseudoPos+n - encryptedStartOffset) / 0x200; // always positive - resetAndSkip(blocksToSkipCountingFromStart); - - long leftovers = realCountOfBytesToSkip % 0x200; // most likely will be 0; TODO: a lot of tests - long bytesToSkipTillRequiredBlock = realCountOfBytesToSkip - leftovers; - skipLoop(bytesToSkipTillRequiredBlock); - fillDecryptedCache(); - pseudoPos += n; - pointerInsideDecryptedSection = (int) leftovers; - return n; - } - log.trace("4. Pointer Inside + End Position Outside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+n)+")"); - skipLoop(realCountOfBytesToSkip); - pseudoPos += n; - pointerInsideDecryptedSection = 0; - return n; - // just fast-forward to position we need and flush caches - } - - if (isEndPositionInsideEncryptedSection(n)) { //pointer will be inside Encrypted Section, but now outside - log.trace("5. End Position Inside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+n)+")"); - //skip to start if the block we need - long bytesToSkipTillEncryptedBlock = encryptedStartOffset - pseudoPos; - long blocksToSkipCountingFromStart = (n - bytesToSkipTillEncryptedBlock) / 0x200; // always positive - long bytesToSkipTillRequiredBlock = bytesToSkipTillEncryptedBlock + blocksToSkipCountingFromStart * 0x200; - long leftovers = n - bytesToSkipTillRequiredBlock; // most likely will be 0; - - long skipped = super.skip(bytesToSkipTillRequiredBlock); - if (bytesToSkipTillRequiredBlock != skipped) - throw new IOException("Can't skip bytes. To skip: " + - bytesToSkipTillEncryptedBlock + - ".\nActually skipped: " + skipped + - ".\nLeftovers inside encrypted section: " + leftovers); - log.trace("\tBlocks skipped "+blocksToSkipCountingFromStart); - resetAndSkip(blocksToSkipCountingFromStart); - fillDecryptedCache(); - pseudoPos += n; - pointerInsideDecryptedSection = (int) leftovers; - return n; - } - log.trace("6. Not encrypted ("+pseudoPos+"-"+(pseudoPos+n)+")"); - skipLoop(n); - pseudoPos += n; - pointerInsideDecryptedSection = 0; - return n; - } - private void skipLoop(long size) throws IOException{ - long mustSkip = size; - long skipped = 0; - while (mustSkip > 0){ - skipped += super.skip(mustSkip); - mustSkip = size - skipped; - log.trace("Skip loop: skipped: "+skipped+"\tmustSkip "+mustSkip); - } - } - private void resetAndSkip(long blockSum) throws IOException{ - try { - decryptor.resetAndSkip(blockSum); - } - catch (Exception e){ throw new IOException(e); } - } - - @Override - public synchronized int read() throws IOException { - byte[] b = new byte[1]; - if (read(b) != -1) - return b[0]; - return -1; - } - - @Override - public boolean markSupported() { - return false; - } - - @Override - public synchronized void mark(int readlimit) {} - - @Override - public synchronized void reset() throws IOException { - throw new IOException("Not supported"); - } -} \ No newline at end of file diff --git a/src/main/java/libKonogonka/ctraesclassic/AesCtrStream.java b/src/main/java/libKonogonka/ctraesclassic/AesCtrStream.java deleted file mode 100644 index 9c3b35e..0000000 --- a/src/main/java/libKonogonka/ctraesclassic/AesCtrStream.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - Copyright 2019-2023 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 . -*/ -package libKonogonka.ctraesclassic; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; - -import javax.crypto.Cipher; -import javax.crypto.CipherInputStream; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; -import java.io.InputStream; -import java.security.Security; - -@Deprecated -public class AesCtrStream { - private static boolean BCinitialized = false; - - private static void initBCProvider(){ - Security.addProvider(new BouncyCastleProvider()); - BCinitialized = true; - } - private AesCtrStream(){ } - - public static CipherInputStream getStream(String keyString, byte[] IVarray, InputStream inputStream) throws Exception{ - if ( ! BCinitialized) - initBCProvider(); - byte[] keyArray = hexStrToByteArray(keyString); - SecretKeySpec key = new SecretKeySpec(keyArray, "AES"); - Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC"); - IvParameterSpec iv = new IvParameterSpec(IVarray.clone()); - cipher.init(Cipher.DECRYPT_MODE, key, iv); - return new CipherInputStream(inputStream, cipher); - } - - private static byte[] hexStrToByteArray(String s) { - int len = s.length(); - byte[] data = new byte[len / 2]; - for (int i = 0; i < len; i += 2) { - data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) - + Character.digit(s.charAt(i+1), 16)); - } - return data; - } -} diff --git a/src/test/java/libKonogonka/package2/Ini1ExtractTest.java b/src/test/java/libKonogonka/package2/Ini1ExtractTest.java index 90c1c4b..326e254 100644 --- a/src/test/java/libKonogonka/package2/Ini1ExtractTest.java +++ b/src/test/java/libKonogonka/package2/Ini1ExtractTest.java @@ -7,7 +7,7 @@ import libKonogonka.Tools.RomFs.FileSystemEntry; import libKonogonka.Tools.RomFs.RomFsProvider; import libKonogonka.Tools.other.System2.System2Provider; import libKonogonka.Tools.other.System2.ini1.Ini1Provider; -import libKonogonka.ctraes.InFileStreamProducer; +import libKonogonka.aesctr.InFileStreamProducer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/libKonogonka/package2/KernelBinExtractTest.java b/src/test/java/libKonogonka/package2/KernelBinExtractTest.java index 1407988..6b09c36 100644 --- a/src/test/java/libKonogonka/package2/KernelBinExtractTest.java +++ b/src/test/java/libKonogonka/package2/KernelBinExtractTest.java @@ -6,14 +6,13 @@ import libKonogonka.Tools.NCA.NCAProvider; import libKonogonka.Tools.RomFs.FileSystemEntry; import libKonogonka.Tools.RomFs.RomFsProvider; import libKonogonka.Tools.other.System2.System2Provider; -import libKonogonka.ctraes.InFileStreamProducer; +import libKonogonka.aesctr.InFileStreamProducer; import org.junit.jupiter.api.*; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.security.MessageDigest; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; diff --git a/src/test/java/libKonogonka/package2/Kip1ExtractDecompressedTest.java b/src/test/java/libKonogonka/package2/Kip1ExtractDecompressedTest.java index 0b2b26e..dfeaeed 100644 --- a/src/test/java/libKonogonka/package2/Kip1ExtractDecompressedTest.java +++ b/src/test/java/libKonogonka/package2/Kip1ExtractDecompressedTest.java @@ -8,7 +8,7 @@ import libKonogonka.Tools.RomFs.RomFsProvider; import libKonogonka.Tools.other.System2.System2Provider; import libKonogonka.Tools.other.System2.ini1.Ini1Provider; import libKonogonka.Tools.other.System2.ini1.KIP1Provider; -import libKonogonka.ctraes.InFileStreamProducer; +import libKonogonka.aesctr.InFileStreamProducer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/libKonogonka/package2/Kip1ExtractTest.java b/src/test/java/libKonogonka/package2/Kip1ExtractTest.java index 2f04604..8172a63 100644 --- a/src/test/java/libKonogonka/package2/Kip1ExtractTest.java +++ b/src/test/java/libKonogonka/package2/Kip1ExtractTest.java @@ -8,7 +8,7 @@ import libKonogonka.Tools.RomFs.RomFsProvider; import libKonogonka.Tools.other.System2.System2Provider; import libKonogonka.Tools.other.System2.ini1.Ini1Provider; import libKonogonka.Tools.other.System2.ini1.KIP1Provider; -import libKonogonka.ctraes.InFileStreamProducer; +import libKonogonka.aesctr.InFileStreamProducer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/libKonogonka/unsorted/NSPpfs0EncryptedTest.java b/src/test/java/libKonogonka/unsorted/NSPpfs0EncryptedTest.java index 527ac65..8509346 100644 --- a/src/test/java/libKonogonka/unsorted/NSPpfs0EncryptedTest.java +++ b/src/test/java/libKonogonka/unsorted/NSPpfs0EncryptedTest.java @@ -24,8 +24,8 @@ import libKonogonka.TitleKeyChainHolder; import libKonogonka.Tools.NCA.NCAProvider; import libKonogonka.Tools.PFS0.PFS0Provider; import libKonogonka.Tools.PFS0.PFS0subFile; -import libKonogonka.ctraes.AesCtrBufferedInputStream; -import libKonogonka.ctraes.AesCtrDecryptForMediaBlocks; +import libKonogonka.aesctr.AesCtrBufferedInputStream; +import libKonogonka.aesctr.AesCtrDecryptForMediaBlocks; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/libKonogonka/unsorted/Pfs0EncryptedTest.java b/src/test/java/libKonogonka/unsorted/Pfs0EncryptedTest.java index bf39269..d656e02 100644 --- a/src/test/java/libKonogonka/unsorted/Pfs0EncryptedTest.java +++ b/src/test/java/libKonogonka/unsorted/Pfs0EncryptedTest.java @@ -23,8 +23,8 @@ import libKonogonka.RainbowDump; import libKonogonka.Tools.NCA.NCAProvider; import libKonogonka.Tools.PFS0.PFS0Provider; import libKonogonka.Tools.PFS0.PFS0subFile; -import libKonogonka.ctraes.AesCtrBufferedInputStream; -import libKonogonka.ctraes.AesCtrDecryptForMediaBlocks; +import libKonogonka.aesctr.AesCtrBufferedInputStream; +import libKonogonka.aesctr.AesCtrDecryptForMediaBlocks; import org.junit.jupiter.api.*; import java.io.*; diff --git a/src/test/java/libKonogonka/unsorted/RomFsEncryptedTest.java b/src/test/java/libKonogonka/unsorted/RomFsEncryptedTest.java index ebe4ece..4ef5417 100644 --- a/src/test/java/libKonogonka/unsorted/RomFsEncryptedTest.java +++ b/src/test/java/libKonogonka/unsorted/RomFsEncryptedTest.java @@ -18,13 +18,13 @@ */ package libKonogonka.unsorted; -import libKonogonka.ctraes.AesCtrBufferedInputStream; +import libKonogonka.aesctr.AesCtrBufferedInputStream; import libKonogonka.KeyChainHolder; import libKonogonka.RainbowDump; import libKonogonka.Tools.NCA.NCAProvider; import libKonogonka.Tools.NCA.NCASectionTableBlock.NcaFsHeader; import libKonogonka.Tools.RomFs.FileSystemEntry; -import libKonogonka.ctraes.AesCtrDecryptForMediaBlocks; +import libKonogonka.aesctr.AesCtrDecryptForMediaBlocks; import org.junit.jupiter.api.*; import java.io.*;