Implement BufferedInputStream with one AesCtr section inside to work with it as with usual BufferedInputStream.
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
c542a23a8c
commit
3064970220
1 changed files with 246 additions and 0 deletions
246
src/main/java/libKonogonka/ctraes/AesCtrBufferedInputStream.java
Normal file
246
src/main/java/libKonogonka/ctraes/AesCtrBufferedInputStream.java
Normal file
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
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.ctraes;
|
||||
|
||||
import libKonogonka.RainbowDump;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class AesCtrBufferedInputStream extends BufferedInputStream {
|
||||
private final static Logger log = LogManager.getLogger(AesCtrBufferedInputStream.class);
|
||||
|
||||
private final AesCtrDecryptSimple decryptor;
|
||||
private final long mediaOffsetPositionStart;
|
||||
private final long mediaOffsetPositionEnd;
|
||||
|
||||
public AesCtrBufferedInputStream(AesCtrDecryptSimple decryptor,
|
||||
long offsetPosition,
|
||||
long mediaStartOffset,
|
||||
long mediaEndOffset,
|
||||
InputStream inputStream){
|
||||
super(inputStream);
|
||||
this.decryptor = decryptor;
|
||||
this.mediaOffsetPositionStart = offsetPosition + (mediaStartOffset * 0x200);
|
||||
this.mediaOffsetPositionEnd = offsetPosition + (mediaEndOffset * 0x200);
|
||||
|
||||
log.debug("\nOffset Position "+offsetPosition+
|
||||
"\nMediaOffsetPositionStart "+RainbowDump.formatDecHexString(mediaOffsetPositionStart)+
|
||||
"\nMediaOffsetPositionEnd "+RainbowDump.formatDecHexString(mediaOffsetPositionEnd));
|
||||
}
|
||||
|
||||
private byte[] decryptedBytes;
|
||||
private long pseudoPos;
|
||||
private int pointerInsideDecryptedSection;
|
||||
|
||||
@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);
|
||||
|
||||
log.error("!Pointer Inside Decrypted Section "+pointerInsideDecryptedSection+" "+(pointerInsideDecryptedSection+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 // TODO: ONLY IF NON-NULL??
|
||||
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) ((mediaOffsetPositionEnd - pseudoPos) / 0x200);
|
||||
int bytesFromEnd = b.length - bytesFromFirstBlock - middleBlocksCount * 0x200;
|
||||
//1
|
||||
System.arraycopy(decryptedBytes, pointerInsideDecryptedSection, b, 0, bytesFromFirstBlock);
|
||||
//2
|
||||
//System.out.println("\n"+bytesFromFirstBlock+"\n"+ middleBlocksCount+" - "+(middleBlocksCount*0x200)+"\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) (mediaOffsetPositionStart - pos);
|
||||
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 void resetAndSkip(long blockSum) throws IOException{
|
||||
try{
|
||||
decryptor.reset();
|
||||
decryptor.skipNext(blockSum); // recalculate
|
||||
}
|
||||
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. Need block of "+ bytes +" while only " +
|
||||
actuallyRead + " bytes.");
|
||||
return chunkBytes;
|
||||
}
|
||||
|
||||
private boolean isPointerInsideEncryptedSection(){
|
||||
return (pseudoPos >= mediaOffsetPositionStart) && (pseudoPos < mediaOffsetPositionEnd);
|
||||
}
|
||||
private boolean isEndPositionInsideEncryptedSection(long requestedBytesCount){
|
||||
return ((pseudoPos + requestedBytesCount) >= mediaOffsetPositionStart) && ((pseudoPos + requestedBytesCount) < mediaOffsetPositionEnd);
|
||||
}
|
||||
|
||||
@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 - mediaOffsetPositionStart) / 0x200; // always positive
|
||||
resetAndSkip(blocksToSkipCountingFromStart);
|
||||
|
||||
long leftovers = realCountOfBytesToSkip % 0x200; // most likely will be 0; TODO: a lot of tests
|
||||
long bytesToSkipTillRequiredBlock = realCountOfBytesToSkip - leftovers;
|
||||
long skipped = super.skip(bytesToSkipTillRequiredBlock);
|
||||
if (bytesToSkipTillRequiredBlock != skipped)
|
||||
throw new IOException("Can't skip bytes. To skip: " +
|
||||
bytesToSkipTillRequiredBlock +
|
||||
".\nActually skipped: " + skipped +
|
||||
".\nLeftovers inside encrypted section: " + leftovers);
|
||||
fillDecryptedCache();
|
||||
pseudoPos += n;
|
||||
pointerInsideDecryptedSection = (int) leftovers;
|
||||
return n;
|
||||
}
|
||||
log.trace("4. Pointer Inside + End Position Outside Encrypted Section ("+pseudoPos+"-"+(pseudoPos+n)+")");
|
||||
long skipped = super.skip(realCountOfBytesToSkip);
|
||||
if (realCountOfBytesToSkip != skipped)
|
||||
throw new IOException("Can't skip bytes. To skip: " +
|
||||
realCountOfBytesToSkip +
|
||||
".\nActually skipped: " + skipped);
|
||||
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 = mediaOffsetPositionStart - pseudoPos; //TODO:FIX
|
||||
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);
|
||||
resetAndSkip(blocksToSkipCountingFromStart);
|
||||
fillDecryptedCache();
|
||||
pseudoPos += n;
|
||||
pointerInsideDecryptedSection = (int) leftovers;
|
||||
return n;
|
||||
}
|
||||
log.trace("6. Not encrypted ("+pseudoPos+"-"+(pseudoPos+n)+")");
|
||||
long skipped = super.skip(n);
|
||||
pseudoPos += n;
|
||||
pointerInsideDecryptedSection = 0;
|
||||
return skipped;
|
||||
}
|
||||
|
||||
@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");
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue