mirror of
https://github.com/Maschell/JNUSLib.git
synced 2025-02-16 16:29:14 +01:00
Refactor FSTEntry decrypting
This commit is contained in:
parent
8dc4c8cf4d
commit
b432537af7
@ -21,7 +21,6 @@ import java.io.InputStream;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import de.mas.wiiu.jnus.NUSTitle;
|
import de.mas.wiiu.jnus.NUSTitle;
|
||||||
@ -75,6 +74,89 @@ public class FSTDataProviderNUSTitle implements FSTDataProvider, HasNUSTitle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean decryptFSTEntryToStreamHashed(FSTEntry entry, OutputStream outputStream, long offset, long size)
|
||||||
|
throws IOException, CheckSumWrongException, NoSuchAlgorithmException {
|
||||||
|
Content c = title.getTMD().getContentByIndex(entry.getContentIndex());
|
||||||
|
|
||||||
|
long payloadOffset = entry.getFileOffset() + offset;
|
||||||
|
long streamOffset = payloadOffset;
|
||||||
|
long streamFilesize = 0;
|
||||||
|
|
||||||
|
streamOffset = (payloadOffset / 0xFC00) * 0x10000;
|
||||||
|
long offsetInBlock = payloadOffset - ((streamOffset / 0x10000) * 0xFC00);
|
||||||
|
if (offsetInBlock + size < 0xFC00) {
|
||||||
|
streamFilesize = 0x10000L;
|
||||||
|
} else {
|
||||||
|
long curVal = 0x10000;
|
||||||
|
long missing = (size - (0xFC00 - offsetInBlock));
|
||||||
|
|
||||||
|
curVal += (missing / 0xFC00) * 0x10000;
|
||||||
|
|
||||||
|
if (missing % 0xFC00 > 0) {
|
||||||
|
curVal += 0x10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
streamFilesize = curVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
NUSDataProvider dataProvider = title.getDataProvider();
|
||||||
|
InputStream in = dataProvider.readContentAsStream(c, streamOffset, streamFilesize);
|
||||||
|
|
||||||
|
NUSDecryption nusdecryption = new NUSDecryption(title.getTicket().get());
|
||||||
|
|
||||||
|
return nusdecryption.decryptStreamsHashed(in, outputStream, payloadOffset, size, dataProvider.getContentH3Hash(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean decryptFSTEntryToStreamNonHashed(FSTEntry entry, OutputStream outputStream, long offset, long size)
|
||||||
|
throws IOException, CheckSumWrongException, NoSuchAlgorithmException {
|
||||||
|
|
||||||
|
Content c = title.getTMD().getContentByIndex(entry.getContentIndex());
|
||||||
|
|
||||||
|
byte[] IV = new byte[0x10];
|
||||||
|
IV[0] = (byte) ((c.getIndex() >> 8) & 0xFF);
|
||||||
|
IV[1] = (byte) (c.getIndex() & 0xFF);
|
||||||
|
|
||||||
|
long payloadOffset = entry.getFileOffset() + offset;
|
||||||
|
long streamOffset = payloadOffset;
|
||||||
|
long streamFilesize = c.getEncryptedFileSize();
|
||||||
|
|
||||||
|
// if we have an offset we can't calculate the hash anymore
|
||||||
|
// we need a new IV
|
||||||
|
if (streamOffset > 0) {
|
||||||
|
streamFilesize = size;
|
||||||
|
|
||||||
|
streamOffset -= 16;
|
||||||
|
streamFilesize += 16;
|
||||||
|
|
||||||
|
// We need to get the current IV as soon as we get the InputStream.
|
||||||
|
IV = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
NUSDataProvider dataProvider = title.getDataProvider();
|
||||||
|
InputStream in = dataProvider.readContentAsStream(c, streamOffset, streamFilesize);
|
||||||
|
|
||||||
|
if (IV == null) {
|
||||||
|
// If we read with an offset > 16 we need the previous 16 bytes because they are the IV.
|
||||||
|
// The input stream has been prepared to start 16 bytes earlier on this case.
|
||||||
|
int toRead = 16;
|
||||||
|
byte[] data = new byte[toRead];
|
||||||
|
int readTotal = 0;
|
||||||
|
while (readTotal < toRead) {
|
||||||
|
int res = in.read(data, readTotal, toRead - readTotal);
|
||||||
|
StreamUtils.checkForException(in);
|
||||||
|
if (res < 0) {
|
||||||
|
// This should NEVER happen.
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
readTotal += res;
|
||||||
|
}
|
||||||
|
IV = Arrays.copyOfRange(data, 0, toRead);
|
||||||
|
}
|
||||||
|
NUSDecryption nusdecryption = new NUSDecryption(title.getTicket().get());
|
||||||
|
|
||||||
|
return nusdecryption.decryptStreamsNonHashed(in, outputStream, payloadOffset, size, c, IV, size != entry.getFileSize());
|
||||||
|
}
|
||||||
|
|
||||||
private boolean decryptFSTEntryToStream(FSTEntry entry, OutputStream outputStream, long offset, long size)
|
private boolean decryptFSTEntryToStream(FSTEntry entry, OutputStream outputStream, long offset, long size)
|
||||||
throws IOException, CheckSumWrongException, NoSuchAlgorithmException {
|
throws IOException, CheckSumWrongException, NoSuchAlgorithmException {
|
||||||
if (entry.isNotInPackage() || !title.getTicket().isPresent()) {
|
if (entry.isNotInPackage() || !title.getTicket().isPresent()) {
|
||||||
@ -92,78 +174,18 @@ public class FSTDataProviderNUSTitle implements FSTDataProvider, HasNUSTitle {
|
|||||||
|
|
||||||
Content c = title.getTMD().getContentByIndex(entry.getContentIndex());
|
Content c = title.getTMD().getContentByIndex(entry.getContentIndex());
|
||||||
|
|
||||||
long payloadOffset = entry.getFileOffset() + offset;
|
|
||||||
long streamOffset = payloadOffset;
|
|
||||||
long streamFilesize = c.getEncryptedFileSize();
|
|
||||||
|
|
||||||
Optional<byte[]> IV = Optional.empty();
|
|
||||||
|
|
||||||
if (c.isHashed()) {
|
|
||||||
streamOffset = (payloadOffset / 0xFC00) * 0x10000;
|
|
||||||
long offsetInBlock = payloadOffset - ((streamOffset / 0x10000) * 0xFC00);
|
|
||||||
if (offsetInBlock + size < 0xFC00) {
|
|
||||||
streamFilesize = 0x10000L;
|
|
||||||
} else {
|
|
||||||
long curVal = 0x10000;
|
|
||||||
long missing = (size - (0xFC00 - offsetInBlock));
|
|
||||||
|
|
||||||
curVal += (missing / 0xFC00) * 0x10000;
|
|
||||||
|
|
||||||
if (missing % 0xFC00 > 0) {
|
|
||||||
curVal += 0x10000;
|
|
||||||
}
|
|
||||||
|
|
||||||
streamFilesize = curVal;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
byte[] newIV = new byte[0x10];
|
|
||||||
newIV[0] = (byte) ((c.getIndex() >> 8) & 0xFF);
|
|
||||||
newIV[1] = (byte) (c.getIndex() & 0xFF);
|
|
||||||
IV = Optional.of(newIV);
|
|
||||||
|
|
||||||
// if we have an offset we can't calculate the hash anymore
|
|
||||||
// we need a new IV
|
|
||||||
if (streamOffset > 0) {
|
|
||||||
streamFilesize = size;
|
|
||||||
|
|
||||||
streamOffset -= 16;
|
|
||||||
streamFilesize += 16;
|
|
||||||
|
|
||||||
// We need to get the current IV as soon as we get the InputStream.
|
|
||||||
IV = Optional.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NUSDataProvider dataProvider = title.getDataProvider();
|
|
||||||
|
|
||||||
InputStream in = dataProvider.readContentAsStream(c, streamOffset, streamFilesize);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
NUSDecryption nusdecryption = new NUSDecryption(title.getTicket().get());
|
if (c.isEncrypted()) {
|
||||||
Optional<byte[]> h3HashedOpt = Optional.empty();
|
if (c.isHashed()) {
|
||||||
if (c.isHashed()) {
|
return decryptFSTEntryToStreamHashed(entry, outputStream, offset, size);
|
||||||
h3HashedOpt = dataProvider.getContentH3Hash(c);
|
} else {
|
||||||
} else {
|
return decryptFSTEntryToStreamNonHashed(entry, outputStream, offset, size);
|
||||||
if (!IV.isPresent()) {
|
|
||||||
// If we read with an offset > 16 we need the previous 16 bytes because they are the IV.
|
|
||||||
// The input stream has been prepared to start 16 bytes earlier on this case.
|
|
||||||
int toRead = 16;
|
|
||||||
byte[] data = new byte[toRead];
|
|
||||||
int readTotal = 0;
|
|
||||||
while (readTotal < toRead) {
|
|
||||||
int res = in.read(data, readTotal, toRead - readTotal);
|
|
||||||
StreamUtils.checkForException(in);
|
|
||||||
if (res < 0) {
|
|
||||||
// This should NEVER happen.
|
|
||||||
throw new IOException();
|
|
||||||
}
|
|
||||||
readTotal += res;
|
|
||||||
}
|
|
||||||
IV = Optional.of(Arrays.copyOfRange(data, 0, toRead));
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
NUSDecryption nusdecryption = new NUSDecryption(title.getTicket().get());
|
||||||
|
InputStream in = title.getDataProvider().readContentAsStream(c, offset, size);
|
||||||
|
return nusdecryption.decryptStreamsNonEncrypted(in, outputStream, offset, size, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
return nusdecryption.decryptStreams(in, outputStream, payloadOffset, size, c, IV, h3HashedOpt, size != entry.getFileSize());
|
|
||||||
} catch (CheckSumWrongException e) {
|
} catch (CheckSumWrongException e) {
|
||||||
if (c.isUNKNWNFlag1Set()) {
|
if (c.isUNKNWNFlag1Set()) {
|
||||||
log.info("Hash doesn't match. But file is optional. Don't worry.");
|
log.info("Hash doesn't match. But file is optional. Don't worry.");
|
||||||
|
@ -20,7 +20,6 @@ import java.io.FileNotFoundException;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -140,7 +139,7 @@ public class NUSDecryption extends AESDecryption {
|
|||||||
} finally {
|
} finally {
|
||||||
StreamUtils.closeAll(inputStream, outputStream);
|
StreamUtils.closeAll(inputStream, outputStream);
|
||||||
}
|
}
|
||||||
if(written < filesize) {
|
if (written < filesize) {
|
||||||
throw new IOException("Failed to read. Missing " + (filesize - written));
|
throw new IOException("Failed to read. Missing " + (filesize - written));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -225,27 +224,38 @@ public class NUSDecryption extends AESDecryption {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean decryptStreams(InputStream inputStream, OutputStream outputStream, long offset, long size, Content content, Optional<byte[]> IV,
|
public boolean decryptStreamsHashed(InputStream inputStream, OutputStream outputStream, long offset, long size, Optional<byte[]> h3HashHashed)
|
||||||
Optional<byte[]> h3HashHashed, boolean partial) throws IOException, CheckSumWrongException, NoSuchAlgorithmException {
|
throws IOException, CheckSumWrongException, NoSuchAlgorithmException {
|
||||||
|
|
||||||
long encryptedFileSize = content.getEncryptedFileSize();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (content.isEncrypted()) {
|
byte[] h3 = h3HashHashed.orElseThrow(() -> new FileNotFoundException("h3 hash not found."));
|
||||||
if (content.isHashed()) {
|
decryptFileStreamHashed(inputStream, outputStream, offset, size, h3);
|
||||||
byte[] h3 = h3HashHashed.orElseThrow(() -> new FileNotFoundException("h3 hash not found."));
|
} finally {
|
||||||
decryptFileStreamHashed(inputStream, outputStream, offset, size, h3);
|
StreamUtils.closeAll(inputStream, outputStream);
|
||||||
} else {
|
}
|
||||||
byte[] h3Hash = content.getSHA2Hash();
|
|
||||||
// Ignore the h3hash if we don't read the whole file.
|
return true;
|
||||||
if (partial) {
|
}
|
||||||
h3Hash = null;
|
|
||||||
}
|
public boolean decryptStreamsNonEncrypted(InputStream inputStream, OutputStream outputStream, long offset, long size, Content content)
|
||||||
decryptFileStream(inputStream, outputStream, offset, size, IV.orElseThrow(() -> new IOException("Missing IV")), h3Hash, encryptedFileSize);
|
throws IOException, CheckSumWrongException, NoSuchAlgorithmException {
|
||||||
}
|
try {
|
||||||
} else {
|
StreamUtils.saveInputStreamToOutputStreamWithHash(inputStream, outputStream, size, content.getSHA2Hash(), content.getEncryptedFileSize());
|
||||||
StreamUtils.saveInputStreamToOutputStreamWithHash(inputStream, outputStream, size, content.getSHA2Hash(), encryptedFileSize);
|
} finally {
|
||||||
|
StreamUtils.closeAll(inputStream, outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean decryptStreamsNonHashed(InputStream inputStream, OutputStream outputStream, long offset, long size, Content content, byte[] IV,
|
||||||
|
boolean partial) throws IOException, CheckSumWrongException {
|
||||||
|
try {
|
||||||
|
byte[] h3Hash = content.getSHA2Hash();
|
||||||
|
// Ignore the h3hash if we don't read the whole file.
|
||||||
|
if (partial) {
|
||||||
|
h3Hash = null;
|
||||||
}
|
}
|
||||||
|
decryptFileStream(inputStream, outputStream, offset, size, IV, h3Hash, content.getEncryptedFileSize());
|
||||||
} finally {
|
} finally {
|
||||||
StreamUtils.closeAll(inputStream, outputStream);
|
StreamUtils.closeAll(inputStream, outputStream);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user