- The NUSTitleLoaders now don't inherit anymore from a class, but use a static function from NUSTitleLoader. The NUSDataProvider is supplier by a Supplier argument. This simplifies the config.

- Remove the global common key. Now the common key needs to be provided to every loading instance. This way loading with different common keys is much easier (and more robust)
This commit is contained in:
Maschell 2019-04-10 18:47:22 +02:00
parent d08a42719a
commit 651e32e7ba
8 changed files with 69 additions and 105 deletions

View File

@ -1,5 +1,5 @@
/****************************************************************************
* Copyright (C) 2016-2018 Maschell
* Copyright (C) 2016-2019 Maschell
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -17,23 +17,11 @@
package de.mas.wiiu.jnus;
import de.mas.wiiu.jnus.entities.Ticket;
import de.mas.wiiu.jnus.implementations.woomy.WoomyInfo;
import de.mas.wiiu.jnus.implementations.wud.parser.WUDGIPartitionTitle;
import de.mas.wiiu.jnus.implementations.wud.parser.WUDGamePartition;
import de.mas.wiiu.jnus.implementations.wud.parser.WUDInfo;
import lombok.Data;
@Data
public class NUSTitleConfig {
private String inputPath;
private WUDGamePartition WUDGamePartition = null;
private WUDGIPartitionTitle WUDGIPartitionTitle = null;
private WUDInfo WUDInfo;
private Ticket ticket;
private int version = Settings.LATEST_TMD_VERSION;
private long titleID = 0x0L;
private WoomyInfo woomyInfo;
private boolean noDecryption;
private byte[] commonKey;
}

View File

@ -16,8 +16,13 @@
****************************************************************************/
package de.mas.wiiu.jnus;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import de.mas.wiiu.jnus.entities.TMD;
import de.mas.wiiu.jnus.entities.Ticket;
@ -25,19 +30,19 @@ import de.mas.wiiu.jnus.entities.content.Content;
import de.mas.wiiu.jnus.entities.fst.FST;
import de.mas.wiiu.jnus.implementations.NUSDataProvider;
import de.mas.wiiu.jnus.utils.StreamUtils;
import de.mas.wiiu.jnus.utils.Utils;
import de.mas.wiiu.jnus.utils.cryptography.AESDecryption;
import lombok.extern.java.Log;
@Log
abstract class NUSTitleLoader {
protected NUSTitleLoader() {
public class NUSTitleLoader {
private NUSTitleLoader() {
// should be empty
}
public NUSTitle loadNusTitle(NUSTitleConfig config) throws Exception {
public static NUSTitle loadNusTitle(NUSTitleConfig config, Supplier<NUSDataProvider> dataProviderFunction)
throws IOException, ParseException {
NUSTitle result = new NUSTitle();
NUSDataProvider dataProvider = getDataProvider(result, config);
NUSDataProvider dataProvider = dataProviderFunction.get();
result.setDataProvider(dataProvider);
byte[] tmdData = dataProvider.getRawTMD().orElseThrow(() -> new ParseException("No TMD data found", 0));
@ -79,6 +84,4 @@ abstract class NUSTitleLoader {
return result;
}
protected abstract NUSDataProvider getDataProvider(NUSTitle title, NUSTitleConfig config);
}

View File

@ -16,35 +16,34 @@
****************************************************************************/
package de.mas.wiiu.jnus;
import java.io.IOException;
import java.text.ParseException;
import de.mas.wiiu.jnus.entities.Ticket;
import de.mas.wiiu.jnus.implementations.NUSDataProvider;
import de.mas.wiiu.jnus.implementations.NUSDataProviderLocal;
public final class NUSTitleLoaderLocal extends NUSTitleLoader {
public final class NUSTitleLoaderLocal {
private NUSTitleLoaderLocal() {
super();
public static NUSTitle loadNUSTitle(String inputPath, byte[] commonKey) throws Exception {
return loadNUSTitle(inputPath, null, commonKey);
}
public static NUSTitle loadNUSTitle(String inputPath) throws Exception {
return loadNUSTitle(inputPath, null);
public static NUSTitle loadNUSTitle(String inputPath, Ticket ticket) throws IOException, ParseException {
return loadNUSTitle(inputPath, ticket, null);
}
public static NUSTitle loadNUSTitle(String inputPath, Ticket ticket) throws Exception {
NUSTitleLoader loader = new NUSTitleLoaderLocal();
public static NUSTitle loadNUSTitle(String inputPath, Ticket ticket, byte[] commonKey) throws IOException, ParseException {
NUSTitleConfig config = new NUSTitleConfig();
config.setCommonKey(commonKey);
if (ticket != null) {
config.setTicket(ticket);
} else if (commonKey == null) {
throw new IOException("Ticket was null and no commonKey was given");
}
config.setInputPath(inputPath);
return loader.loadNusTitle(config);
}
@Override
protected NUSDataProvider getDataProvider(NUSTitle title, NUSTitleConfig config) {
return new NUSDataProviderLocal(title, config.getInputPath());
return NUSTitleLoader.loadNusTitle(config, () -> new NUSDataProviderLocal(inputPath));
}
}

View File

@ -18,25 +18,14 @@
package de.mas.wiiu.jnus;
import de.mas.wiiu.jnus.entities.Ticket;
import de.mas.wiiu.jnus.implementations.NUSDataProvider;
import de.mas.wiiu.jnus.implementations.NUSDataProviderLocalBackup;
public final class NUSTitleLoaderLocalBackup extends NUSTitleLoader {
public final class NUSTitleLoaderLocalBackup {
private NUSTitleLoaderLocalBackup() {
super();
}
public static NUSTitle loadNUSTitle(String inputPath) throws Exception {
return loadNUSTitle(inputPath, (short) Settings.LATEST_TMD_VERSION);
}
public static NUSTitle loadNUSTitle(String inputPath, short titleVersion) throws Exception {
return loadNUSTitle(inputPath, titleVersion, null);
}
public static NUSTitle loadNUSTitle(String inputPath, short titleVersion, Ticket ticket) throws Exception {
NUSTitleLoader loader = new NUSTitleLoaderLocalBackup();
NUSTitleConfig config = new NUSTitleConfig();
if (ticket != null) {
@ -45,15 +34,7 @@ public final class NUSTitleLoaderLocalBackup extends NUSTitleLoader {
config.setNoDecryption(true);
}
config.setVersion(titleVersion);
config.setInputPath(inputPath);
return loader.loadNusTitle(config);
}
@Override
protected NUSDataProvider getDataProvider(NUSTitle title, NUSTitleConfig config) {
return new NUSDataProviderLocalBackup(title, config.getInputPath(), (short) config.getVersion());
return NUSTitleLoader.loadNusTitle(config, () -> new NUSDataProviderLocalBackup(inputPath, titleVersion));
}
}

View File

@ -16,22 +16,23 @@
****************************************************************************/
package de.mas.wiiu.jnus;
import java.io.IOException;
import java.text.ParseException;
import de.mas.wiiu.jnus.entities.Ticket;
import de.mas.wiiu.jnus.implementations.NUSDataProvider;
import de.mas.wiiu.jnus.implementations.NUSDataProviderRemote;
public final class NUSTitleLoaderRemote extends NUSTitleLoader {
public final class NUSTitleLoaderRemote {
private NUSTitleLoaderRemote() {
super();
}
public static NUSTitle loadNUSTitle(long titleID) throws Exception {
return loadNUSTitle(titleID, Settings.LATEST_TMD_VERSION, null);
public static NUSTitle loadNUSTitle(long titleID, byte[] commonKey) throws Exception {
return loadNUSTitle(titleID, Settings.LATEST_TMD_VERSION, commonKey);
}
public static NUSTitle loadNUSTitle(long titleID, int version) throws Exception {
return loadNUSTitle(titleID, version, null);
public static NUSTitle loadNUSTitle(long titleID, int version, byte[] commonKey) throws Exception {
return loadNUSTitle(titleID, version, null, false, commonKey);
}
public static NUSTitle loadNUSTitle(long titleID, Ticket ticket) throws Exception {
@ -39,24 +40,20 @@ public final class NUSTitleLoaderRemote extends NUSTitleLoader {
}
public static NUSTitle loadNUSTitle(long titleID, int version, Ticket ticket) throws Exception {
return loadNUSTitle(titleID, version, ticket, false);
return loadNUSTitle(titleID, version, ticket, false, null);
}
public static NUSTitle loadNUSTitle(long titleID, int version, Ticket ticket, boolean noEncryption) throws Exception {
NUSTitleLoader loader = new NUSTitleLoaderRemote();
public static NUSTitle loadNUSTitle(long titleID, int version, Ticket ticket, boolean noEncryption, byte[] commonKey) throws IOException, ParseException {
NUSTitleConfig config = new NUSTitleConfig();
config.setVersion(version);
config.setTitleID(titleID);
config.setTicket(ticket);
config.setNoDecryption(noEncryption);
config.setCommonKey(commonKey);
if (ticket == null && !noEncryption && commonKey == null) {
throw new IOException("Ticket was null and no commonKey was given");
}
return loader.loadNusTitle(config);
}
@Override
protected NUSDataProvider getDataProvider(NUSTitle title, NUSTitleConfig config) {
return new NUSDataProviderRemote(title, config.getVersion(), config.getTitleID());
return NUSTitleLoader.loadNusTitle(config, () -> new NUSDataProviderRemote(version, titleID));
}
}

View File

@ -1,5 +1,5 @@
/****************************************************************************
* Copyright (C) 2016-2018 Maschell
* Copyright (C) 2016-2019 Maschell
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -17,32 +17,29 @@
package de.mas.wiiu.jnus;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import de.mas.wiiu.jnus.implementations.NUSDataProvider;
import de.mas.wiiu.jnus.implementations.NUSDataProviderWoomy;
import de.mas.wiiu.jnus.implementations.woomy.WoomyInfo;
import de.mas.wiiu.jnus.implementations.woomy.WoomyParser;
import lombok.extern.java.Log;
@Log
public final class NUSTitleLoaderWoomy extends NUSTitleLoader {
public final class NUSTitleLoaderWoomy {
public static NUSTitle loadNUSTitle(String inputFile) throws Exception {
NUSTitleLoaderWoomy loader = new NUSTitleLoaderWoomy();
private NUSTitleLoaderWoomy() {
}
public static NUSTitle loadNUSTitle(String inputFile) throws IOException, ParserConfigurationException, SAXException, ParseException {
NUSTitleConfig config = new NUSTitleConfig();
WoomyInfo woomyInfo = WoomyParser.createWoomyInfo(new File(inputFile));
if (woomyInfo == null) {
log.info("Created woomy is null.");
return null;
}
config.setWoomyInfo(woomyInfo);
return loader.loadNusTitle(config);
}
@Override
protected NUSDataProvider getDataProvider(NUSTitle title, NUSTitleConfig config) {
return new NUSDataProviderWoomy(title, config.getWoomyInfo());
return NUSTitleLoader.loadNusTitle(config, () -> new NUSDataProviderWoomy(woomyInfo));
}
}

View File

@ -31,6 +31,5 @@ public class Settings {
public static final String USER_AGENT = "Mozilla/5.0 (Nintendo WiiU) AppleWebKit/536.28 (KHTML, like Gecko) NX/3.0.3.12.12 NintendoBrowser/3.0.0.9561.US";
public static final boolean ALLOW_PARALLELISATION = true;
public static byte[] commonKey = new byte[0x10];
public static int WIIU_DECRYPTED_AREA_OFFSET = 0x18000;
}

View File

@ -22,7 +22,6 @@ import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.util.Arrays;
import de.mas.wiiu.jnus.Settings;
import de.mas.wiiu.jnus.utils.Utils;
import de.mas.wiiu.jnus.utils.cryptography.AESDecryption;
import lombok.Getter;
@ -44,17 +43,17 @@ public final class Ticket {
this.IV = IV;
}
public static Ticket parseTicket(File ticket) throws IOException {
public static Ticket parseTicket(File ticket, byte[] commonKey) throws IOException {
if (ticket == null || !ticket.exists()) {
log.warning("Ticket input file null or doesn't exist.");
return null;
throw new IOException("Ticket input file null or doesn't exist.");
}
return parseTicket(Files.readAllBytes(ticket.toPath()));
return parseTicket(Files.readAllBytes(ticket.toPath()), commonKey);
}
public static Ticket parseTicket(byte[] ticket) throws IOException {
public static Ticket parseTicket(byte[] ticket, byte[] commonKey) throws IOException {
if (ticket == null) {
return null;
throw new IOException("Ticket input file null or doesn't exist.");
}
ByteBuffer buffer = ByteBuffer.allocate(ticket.length);
@ -69,20 +68,20 @@ public final class Ticket {
buffer.position(POSITION_TITLEID);
long titleID = buffer.getLong();
Ticket result = createTicket(encryptedKey, titleID);
Ticket result = createTicket(encryptedKey, titleID, commonKey);
return result;
}
public static Ticket createTicket(byte[] encryptedKey, long titleID) {
public static Ticket createTicket(byte[] encryptedKey, long titleID, byte[] commonKey) {
byte[] IV = ByteBuffer.allocate(0x10).putLong(titleID).array();
byte[] decryptedKey = calculateDecryptedKey(encryptedKey, IV);
byte[] decryptedKey = calculateDecryptedKey(encryptedKey, IV, commonKey);
return new Ticket(encryptedKey, decryptedKey, IV);
}
private static byte[] calculateDecryptedKey(byte[] encryptedKey, byte[] IV) {
AESDecryption decryption = new AESDecryption(Settings.commonKey, IV);
private static byte[] calculateDecryptedKey(byte[] encryptedKey, byte[] IV, byte[] commonKey) {
AESDecryption decryption = new AESDecryption(commonKey, IV);
return decryption.decrypt(encryptedKey);
}
@ -107,4 +106,5 @@ public final class Ticket {
public String toString() {
return "Ticket [encryptedKey=" + Utils.ByteArrayToString(encryptedKey) + ", decryptedKey=" + Utils.ByteArrayToString(decryptedKey) + "]";
}
}