mirror of
https://github.com/SabreTools/NDecrypt.git
synced 2026-07-09 02:16:52 +00:00
Fix reading CIA header info
This commit is contained in:
@@ -192,7 +192,7 @@ More than one path can be specified at a time.");
|
||||
Console.WriteLine("File recognized as Nintendo 3DS");
|
||||
return new ThreeDSTool(filename, decryptArgs);
|
||||
// case FileType.N3DSCIA:
|
||||
// Console.WriteLine("File recognized as Nintendo 3DS");
|
||||
// Console.WriteLine("File recognized as Nintendo 3DS CIA");
|
||||
// return new CIATool(filename, decryptArgs);
|
||||
case FileType.NULL:
|
||||
default:
|
||||
|
||||
@@ -5,7 +5,7 @@ using NDecrypt.N3DS.Headers;
|
||||
namespace NDecrypt.N3DS
|
||||
{
|
||||
// https://www.3dbrew.org/wiki/CIA
|
||||
internal class CIATool : ITool
|
||||
public class CIATool : ITool
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the input CIA file
|
||||
|
||||
@@ -186,8 +186,8 @@ namespace NDecrypt.N3DS
|
||||
internal enum PublicKeyType : uint
|
||||
{
|
||||
RSA_4096 = 0x00000000,
|
||||
RSA_2048 = 0x00000001,
|
||||
ECDSA = 0x00000002,
|
||||
RSA_2048 = 0x01000000,
|
||||
ECDSA = 0x02000000,
|
||||
}
|
||||
|
||||
internal enum ResourceLimitCategory
|
||||
@@ -198,14 +198,15 @@ namespace NDecrypt.N3DS
|
||||
OTHER = 3,
|
||||
}
|
||||
|
||||
// Note: These are reversed because of how C# reads values
|
||||
internal enum SignatureType : uint
|
||||
{
|
||||
RSA_4096_SHA1 = 0x010000,
|
||||
RSA_2048_SHA1 = 0x010001,
|
||||
ECDSA_SHA1 = 0x010002,
|
||||
RSA_4096_SHA256 = 0x010003,
|
||||
RSA_2048_SHA256 = 0x010004,
|
||||
ECDSA_SHA256 = 0x010005,
|
||||
RSA_4096_SHA1 = 0x00000100,
|
||||
RSA_2048_SHA1 = 0x01000100,
|
||||
ECDSA_SHA1 = 0x02000100,
|
||||
RSA_4096_SHA256 = 0x03000100,
|
||||
RSA_2048_SHA256 = 0x04000100,
|
||||
ECDSA_SHA256 = 0x05000100,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
@@ -8,7 +9,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Archive header size, usually 0x2020 bytes
|
||||
/// </summary>
|
||||
public uint HeaderSize { get; private set; }
|
||||
public int HeaderSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Type
|
||||
@@ -45,6 +46,11 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// </summary>
|
||||
public long ContentSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Content Index
|
||||
/// </summary>
|
||||
public int ContentIndex { get; private set; }
|
||||
|
||||
#region Content Index
|
||||
|
||||
/// <summary>
|
||||
@@ -68,10 +74,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Content file data
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Probably not going to be read in fully later
|
||||
/// </remarks>
|
||||
public byte[] ContentFileData { get; set; }
|
||||
public NCCHHeader ContentFileData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Meta file data (Not a necessary component)
|
||||
@@ -91,7 +94,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
|
||||
try
|
||||
{
|
||||
header.HeaderSize = reader.ReadUInt32();
|
||||
header.HeaderSize = reader.ReadInt32();
|
||||
header.Type = reader.ReadUInt16();
|
||||
header.Version = reader.ReadUInt16();
|
||||
header.CertificateChainSize = reader.ReadInt32();
|
||||
@@ -99,16 +102,33 @@ namespace NDecrypt.N3DS.Headers
|
||||
header.TMDFileSize = reader.ReadInt32();
|
||||
header.MetaSize = reader.ReadInt32();
|
||||
header.ContentSize = reader.ReadInt64();
|
||||
header.ContentIndex = reader.ReadInt32();
|
||||
reader.ReadBytes(0x2000); // TODO: Not sure what's in the Content Index area
|
||||
if (reader.BaseStream.Position % 64 != 0)
|
||||
reader.BaseStream.Seek(64 - (reader.BaseStream.Position % 64), SeekOrigin.Current);
|
||||
|
||||
header.CertificateChain = new Certificate[3];
|
||||
header.CertificateChain[0] = Certificate.Read(reader); // CA
|
||||
header.CertificateChain[1] = Certificate.Read(reader); // Ticket
|
||||
header.CertificateChain[2] = Certificate.Read(reader); // TMD
|
||||
if (reader.BaseStream.Position % 64 != 0)
|
||||
reader.BaseStream.Seek(64 - (reader.BaseStream.Position % 64), SeekOrigin.Current);
|
||||
|
||||
header.Ticket = Ticket.Read(reader, header.TicketSize);
|
||||
if (reader.BaseStream.Position % 64 != 0)
|
||||
reader.BaseStream.Seek(64 - (reader.BaseStream.Position % 64), SeekOrigin.Current);
|
||||
|
||||
header.TMDFileData = TitleMetadata.Read(reader, header.TMDFileSize);
|
||||
header.ContentFileData = reader.ReadBytes((int)header.ContentSize);
|
||||
|
||||
if (reader.BaseStream.Position % 64 != 0)
|
||||
reader.BaseStream.Seek(64 - (reader.BaseStream.Position % 64), SeekOrigin.Current);
|
||||
|
||||
header.ContentFileData = NCCHHeader.Read(reader, readSignature: true);
|
||||
if (header.ContentFileData == null)
|
||||
{
|
||||
Console.WriteLine($"CIA content file data error: Unable to read NCCH header");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (header.MetaSize > 0)
|
||||
header.MetaFileData = MetaFile.Read(reader);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
@@ -30,6 +31,13 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// </summary>
|
||||
public byte[] Issuer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Issuer as a trimmed string
|
||||
/// </summary>
|
||||
public string IssuerString => Issuer != null && Issuer.Length > 0
|
||||
? Encoding.ASCII.GetString(Issuer)?.TrimEnd('\0')
|
||||
: null;
|
||||
|
||||
/// <summary>
|
||||
/// Key Type
|
||||
/// </summary>
|
||||
@@ -40,6 +48,13 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// </summary>
|
||||
public byte[] Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name as a trimmed string
|
||||
/// </summary>
|
||||
public string NameString => Name != null && Name.Length > 0
|
||||
? Encoding.ASCII.GetString(Name)?.TrimEnd('\0')
|
||||
: null;
|
||||
|
||||
/// <summary>
|
||||
/// Expiration time as UNIX Timestamp, used at least for CTCert
|
||||
/// </summary>
|
||||
@@ -99,6 +114,8 @@ namespace NDecrypt.N3DS.Headers
|
||||
ct.SignatureSize = 0x03C;
|
||||
ct.PaddingSize = 0x40;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
ct.Signature = reader.ReadBytes(ct.SignatureSize);
|
||||
@@ -116,14 +133,18 @@ namespace NDecrypt.N3DS.Headers
|
||||
reader.ReadBytes(0x34); // Padding
|
||||
break;
|
||||
case PublicKeyType.RSA_2048:
|
||||
case PublicKeyType.RSA_2048_REV:
|
||||
ct.Modulus = reader.ReadBytes(0x100);
|
||||
ct.PublicExponent = reader.ReadUInt32();
|
||||
reader.ReadBytes(0x34); // Padding
|
||||
break;
|
||||
case PublicKeyType.ECDSA:
|
||||
case PublicKeyType.ECDSA_REV:
|
||||
ct.PublicKey = reader.ReadBytes(0x3C);
|
||||
reader.ReadBytes(0x3C); // Padding
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
return ct;
|
||||
|
||||
@@ -64,12 +64,12 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// <summary>
|
||||
/// Maker code
|
||||
/// </summary>
|
||||
public byte[] MakerCode { get; private set; }
|
||||
public ushort MakerCode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Version
|
||||
/// </summary>
|
||||
public byte[] Version { get; private set; }
|
||||
public ushort Version { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// When ncchflag[7] = 0x20 starting with FIRM 9.6.0-X, this is compared with the first output u32 from a
|
||||
@@ -77,7 +77,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// [programID from NCCH + 0x118]. This hash is only used for verification of the content lock seed, and
|
||||
/// is not the actual keyY.
|
||||
/// </summary>
|
||||
public byte[] VerificationHash { get; private set; }
|
||||
public uint VerificationHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Program ID
|
||||
@@ -211,9 +211,9 @@ namespace NDecrypt.N3DS.Headers
|
||||
|
||||
header.ContentSizeInMediaUnits = reader.ReadUInt32();
|
||||
header.PartitionId = reader.ReadBytes(8).Reverse().ToArray();
|
||||
header.MakerCode = reader.ReadBytes(2);
|
||||
header.Version = reader.ReadBytes(2);
|
||||
header.VerificationHash = reader.ReadBytes(4);
|
||||
header.MakerCode = reader.ReadUInt16();
|
||||
header.Version = reader.ReadUInt16();
|
||||
header.VerificationHash = reader.ReadUInt32();
|
||||
header.ProgramId = reader.ReadBytes(8);
|
||||
header.Reserved1 = reader.ReadBytes(0x10);
|
||||
header.LogoRegionHash = reader.ReadBytes(0x20);
|
||||
|
||||
@@ -299,7 +299,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
header.CardSeedAESMAC = reader.ReadBytes(0x10);
|
||||
header.CardSeedNonce = reader.ReadBytes(0xC);
|
||||
header.Reserved5 = reader.ReadBytes(0xC4);
|
||||
header.BackupHeader = NCCHHeader.Read(reader, false);
|
||||
header.BackupHeader = NCCHHeader.Read(reader, readSignature: false);
|
||||
|
||||
if (development)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
@@ -30,6 +33,13 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// </summary>
|
||||
public byte[] Issuer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Issuer as a trimmed string
|
||||
/// </summary>
|
||||
public string IssuerString => Issuer != null && Issuer.Length > 0
|
||||
? Encoding.ASCII.GetString(Issuer)?.TrimEnd('\0')
|
||||
: null;
|
||||
|
||||
/// <summary>
|
||||
/// ECC PublicKey
|
||||
/// </summary>
|
||||
@@ -166,16 +176,6 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// </summary>
|
||||
public int ContentIndexSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Content Index
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The Content Index of a ticket has its own size defined within itself,
|
||||
/// with seemingly a minimal of 20 bytes, the second u32 in big endian defines
|
||||
/// the full value of X.
|
||||
/// </remarks>
|
||||
public byte[] ContentIndex { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Certificate chain
|
||||
/// </summary>
|
||||
@@ -196,6 +196,8 @@ namespace NDecrypt.N3DS.Headers
|
||||
|
||||
try
|
||||
{
|
||||
long startingPosition = reader.BaseStream.Position;
|
||||
|
||||
tk.SignatureType = (SignatureType)reader.ReadUInt32();
|
||||
switch (tk.SignatureType)
|
||||
{
|
||||
@@ -214,6 +216,8 @@ namespace NDecrypt.N3DS.Headers
|
||||
tk.SignatureSize = 0x03C;
|
||||
tk.PaddingSize = 0x40;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
tk.Signature = reader.ReadBytes(tk.SignatureSize);
|
||||
@@ -245,12 +249,14 @@ namespace NDecrypt.N3DS.Headers
|
||||
tk.Limits[i] = reader.ReadInt32();
|
||||
}
|
||||
|
||||
reader.ReadBytes(4);
|
||||
tk.ContentIndexSize = reader.ReadInt32();
|
||||
reader.ReadBytes(4); // Seek to size in Content Index
|
||||
tk.ContentIndexSize = BitConverter.ToInt32(reader.ReadBytes(4).Reverse().ToArray(), 0);
|
||||
reader.BaseStream.Seek(-8, SeekOrigin.Current);
|
||||
tk.ContentIndex = reader.ReadBytes(tk.ContentIndexSize);
|
||||
reader.ReadBytes(tk.ContentIndexSize); // TODO: Not sure what's in the Content Index area
|
||||
if (reader.BaseStream.Position % 64 != 0)
|
||||
reader.BaseStream.Seek(64 - (reader.BaseStream.Position % 64), SeekOrigin.Current);
|
||||
|
||||
if (ticketSize - (0x164 + tk.ContentIndexSize) > 0)
|
||||
if (ticketSize > (reader.BaseStream.Position - startingPosition) + (2 * 0x200))
|
||||
{
|
||||
tk.CertificateChain = new Certificate[2];
|
||||
tk.CertificateChain[0] = Certificate.Read(reader); // Ticket
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
@@ -30,6 +33,13 @@ namespace NDecrypt.N3DS.Headers
|
||||
/// </summary>
|
||||
public byte[] SignatureIssuer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Signature Issuer as a trimmed string
|
||||
/// </summary>
|
||||
public string SignatureIssuerString => SignatureIssuer != null && SignatureIssuer.Length > 0
|
||||
? Encoding.ASCII.GetString(SignatureIssuer)?.TrimEnd('\0')
|
||||
: null;
|
||||
|
||||
/// <summary>
|
||||
/// Version
|
||||
/// </summary>
|
||||
@@ -156,6 +166,8 @@ namespace NDecrypt.N3DS.Headers
|
||||
|
||||
try
|
||||
{
|
||||
long startingPosition = reader.BaseStream.Position;
|
||||
|
||||
tm.SignatureType = (SignatureType)reader.ReadUInt32();
|
||||
switch (tm.SignatureType)
|
||||
{
|
||||
@@ -194,7 +206,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
tm.Reserved3 = reader.ReadBytes(0x31);
|
||||
tm.AccessRights = reader.ReadUInt32();
|
||||
tm.TitleVersion = reader.ReadUInt16();
|
||||
tm.ContentCount = reader.ReadUInt16();
|
||||
tm.ContentCount = BitConverter.ToUInt16(reader.ReadBytes(2).Reverse().ToArray(), 0);
|
||||
tm.BootContent = reader.ReadUInt16();
|
||||
tm.Padding = reader.ReadUInt16();
|
||||
tm.SHA256HashContentInfoRecords = reader.ReadBytes(0x20);
|
||||
@@ -211,7 +223,7 @@ namespace NDecrypt.N3DS.Headers
|
||||
tm.ContentChunkRecords[i] = ContentChunkRecord.Read(reader);
|
||||
}
|
||||
|
||||
if (metadataSize - (0xC4 + (64 * 0x24) + (tm.ContentCount * 0x30)) > 0)
|
||||
if (metadataSize > (reader.BaseStream.Position - startingPosition) + (2 * 0x200))
|
||||
{
|
||||
tm.CertificateChain = new Certificate[2];
|
||||
tm.CertificateChain[0] = Certificate.Read(reader); // TMD
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace NDecrypt.N3DS
|
||||
// Seek to the beginning of the NCCH partition
|
||||
reader.BaseStream.Seek((ncsdHeader.PartitionsTable[partitionNumber].Offset * ncsdHeader.MediaUnitSize), SeekOrigin.Begin);
|
||||
|
||||
NCCHHeader partitionHeader = NCCHHeader.Read(reader, true);
|
||||
NCCHHeader partitionHeader = NCCHHeader.Read(reader, readSignature: true);
|
||||
if (partitionHeader == null)
|
||||
{
|
||||
Console.WriteLine($"Partition {partitionNumber} Unable to read NCCH header");
|
||||
|
||||
Reference in New Issue
Block a user