149 lines
5.2 KiB
Java
149 lines
5.2 KiB
Java
/*
|
|
Copyright 2019-2020 Dmitry Isaenko
|
|
|
|
This file is part of Konogonka.
|
|
|
|
Konogonka 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.
|
|
|
|
Konogonka 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 Konogonka. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
package libKonogonka.Tools.XCI;
|
|
|
|
import java.util.Arrays;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
|
|
import static libKonogonka.LoperConverter.getLEint;
|
|
import static libKonogonka.LoperConverter.getLElong;
|
|
/**
|
|
* Header information
|
|
* */
|
|
public class XCIGamecardHeader{
|
|
private byte[] rsa2048PKCS1sig;
|
|
private boolean magicHead;
|
|
private byte[] SecureAreaStartAddr;
|
|
private boolean bkupAreaStartAddr;
|
|
private byte titleKEKIndexBoth;
|
|
private byte titleKEKIndex;
|
|
private byte KEKIndex;
|
|
private byte gcSize;
|
|
private byte gcVersion;
|
|
private byte gcFlags;
|
|
private byte[] pkgID;
|
|
private long valDataEndAddr;
|
|
private byte[] gcInfoIV;
|
|
private long hfs0partOffset;
|
|
private long hfs0headerSize;
|
|
private byte[] hfs0headerSHA256;
|
|
private byte[] hfs0initDataSHA256 ;
|
|
private int secureModeFlag;
|
|
private int titleKeyFlag;
|
|
private int keyFlag;
|
|
private byte[] normAreaEndAddr;
|
|
|
|
XCIGamecardHeader(byte[] headerBytes) throws Exception{
|
|
if (headerBytes.length != 400)
|
|
throw new Exception("XCIGamecardHeader Incorrect array size. Expected 400 bytes while received "+headerBytes.length);
|
|
rsa2048PKCS1sig = Arrays.copyOfRange(headerBytes, 0, 256);
|
|
magicHead = Arrays.equals(Arrays.copyOfRange(headerBytes, 256, 260), new byte[]{0x48, 0x45, 0x41, 0x44});
|
|
SecureAreaStartAddr = Arrays.copyOfRange(headerBytes, 260, 264);
|
|
bkupAreaStartAddr = Arrays.equals(Arrays.copyOfRange(headerBytes, 264, 268), new byte[]{(byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff});
|
|
titleKEKIndexBoth = headerBytes[268];
|
|
titleKEKIndex = (byte) ((titleKEKIndexBoth >> 4) & (byte) 0x0F);
|
|
KEKIndex = (byte) (titleKEKIndexBoth & 0x0F);
|
|
gcSize = headerBytes[269];
|
|
gcVersion = headerBytes[270];
|
|
gcFlags = headerBytes[271];
|
|
pkgID = Arrays.copyOfRange(headerBytes, 272, 280);
|
|
valDataEndAddr = getLElong(headerBytes, 280); //TODO: FIX/simplify //
|
|
gcInfoIV = reverseBytes(Arrays.copyOfRange(headerBytes, 288, 304));
|
|
hfs0partOffset = getLElong(headerBytes, 304);
|
|
hfs0headerSize = getLElong(headerBytes, 312);
|
|
hfs0headerSHA256 = Arrays.copyOfRange(headerBytes, 320, 352);
|
|
hfs0initDataSHA256 = Arrays.copyOfRange(headerBytes, 352, 384);
|
|
secureModeFlag = getLEint(headerBytes, 384);
|
|
titleKeyFlag = getLEint(headerBytes, 388);
|
|
keyFlag = getLEint(headerBytes, 392);
|
|
normAreaEndAddr = Arrays.copyOfRange(headerBytes, 396, 400);
|
|
}
|
|
|
|
public byte[] getRsa2048PKCS1sig() { return rsa2048PKCS1sig; }
|
|
public boolean isMagicHeadOk() { return magicHead; }
|
|
public byte[] getSecureAreaStartAddr() { return SecureAreaStartAddr; }
|
|
public boolean isBkupAreaStartAddrOk() { return bkupAreaStartAddr; }
|
|
public byte getTitleKEKIndexBoth() { return titleKEKIndexBoth; }
|
|
public byte getTitleKEKIndex() { return titleKEKIndex; }
|
|
public byte getKEKIndex() { return KEKIndex; }
|
|
|
|
public byte getGcSize() {
|
|
return gcSize;
|
|
}
|
|
public byte getGcVersion() {
|
|
return gcVersion;
|
|
}
|
|
public byte getGcFlags() {
|
|
return gcFlags;
|
|
}
|
|
public byte[] getPkgID() {
|
|
return pkgID;
|
|
}
|
|
public long getValDataEndAddr() {
|
|
return valDataEndAddr;
|
|
}
|
|
public byte[] getGcInfoIV() {
|
|
return gcInfoIV;
|
|
}
|
|
public long getHfs0partOffset() {
|
|
return hfs0partOffset;
|
|
}
|
|
public long getHfs0headerSize() {
|
|
return hfs0headerSize;
|
|
}
|
|
public byte[] getHfs0headerSHA256() {
|
|
return hfs0headerSHA256;
|
|
}
|
|
public byte[] getHfs0initDataSHA256() {
|
|
return hfs0initDataSHA256;
|
|
}
|
|
public int getSecureModeFlag() {
|
|
return secureModeFlag;
|
|
}
|
|
public boolean isSecureModeFlagOk(){
|
|
return secureModeFlag == 1;
|
|
}
|
|
public int getTitleKeyFlag() {
|
|
return titleKeyFlag;
|
|
}
|
|
public boolean istitleKeyFlagOk(){
|
|
return titleKeyFlag == 2;
|
|
}
|
|
public int getKeyFlag() {
|
|
return keyFlag;
|
|
}
|
|
public boolean iskeyFlagOk(){
|
|
return keyFlag == 0;
|
|
}
|
|
public byte[] getNormAreaEndAddr() {
|
|
return normAreaEndAddr;
|
|
}
|
|
|
|
private byte[] reverseBytes(byte[] bArr){
|
|
Byte[] objArr = new Byte[bArr.length];
|
|
for (int i=0;i < bArr.length; i++)
|
|
objArr[i] = bArr[i];
|
|
List<Byte> bytesList = Arrays.asList(objArr);
|
|
Collections.reverse(bytesList);
|
|
for (int i=0;i < bArr.length; i++)
|
|
bArr[i] = objArr[i];
|
|
return bArr;
|
|
}
|
|
} |