AES CTR refactor, simplify, unify
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
5bb6a3c3f4
commit
6e4e4dd3e7
32 changed files with 204 additions and 513 deletions
|
@ -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){
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
package libKonogonka.Tools;
|
||||
|
||||
import libKonogonka.ctraes.InFileStreamProducer;
|
||||
import libKonogonka.aesctr.InFileStreamProducer;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
package libKonogonka.Tools.NSO;
|
||||
|
||||
import libKonogonka.ctraes.InFileStreamProducer;
|
||||
import libKonogonka.aesctr.InFileStreamProducer;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
package libKonogonka.Tools.RomFs;
|
||||
|
||||
import libKonogonka.Converter;
|
||||
import libKonogonka.ctraes.InFileStreamProducer;
|
||||
import libKonogonka.aesctr.InFileStreamProducer;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.*;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
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;
|
||||
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];
|
32
src/main/java/libKonogonka/aesctr/AesCtrDecrypt.java
Normal file
32
src/main/java/libKonogonka/aesctr/AesCtrDecrypt.java
Normal file
|
@ -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;
|
||||
}
|
|
@ -16,51 +16,35 @@
|
|||
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.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;
|
||||
}
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -16,10 +16,9 @@
|
|||
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.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))
|
|
@ -16,7 +16,7 @@
|
|||
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;
|
||||
package libKonogonka.aesctr;
|
||||
|
||||
import libKonogonka.IProducer;
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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");
|
||||
}
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.*;
|
||||
|
|
|
@ -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.*;
|
||||
|
|
Loading…
Reference in a new issue