mirror of
https://github.com/aaru-dps/Aaru.git
synced 2026-07-08 17:56:18 +00:00
[NDIF] Add CRC verification. Fixes #88
This commit is contained in:
12
Aaru.Images/Localization/Localization.Designer.cs
generated
12
Aaru.Images/Localization/Localization.Designer.cs
generated
@@ -6135,5 +6135,17 @@ namespace Aaru.Images {
|
||||
return ResourceManager.GetString("MagicIso_NRG_trailer_has_no_tracks", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string Verifying_NDIF_image_CRC {
|
||||
get {
|
||||
return ResourceManager.GetString("Verifying_NDIF_image_CRC", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string NDIF_CRC_mismatch_expected_0_got_1 {
|
||||
get {
|
||||
return ResourceManager.GetString("NDIF_CRC_mismatch_expected_0_got_1", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3060,4 +3060,10 @@
|
||||
<data name="MagicIso_NRG_trailer_has_no_tracks" xml:space="preserve">
|
||||
<value>El bloque NRG incrustado en la imagen UIF de MagicISO no contiene pistas.</value>
|
||||
</data>
|
||||
<data name="Verifying_NDIF_image_CRC" xml:space="preserve">
|
||||
<value>[slateblue1]Verificando el CRC de la imagen NDIF…[/]</value>
|
||||
</data>
|
||||
<data name="NDIF_CRC_mismatch_expected_0_got_1" xml:space="preserve">
|
||||
<value>CRC de la imagen NDIF no coincide: se esperaba 0x{0:X7}, se obtuvo 0x{1:X7}</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -3079,4 +3079,10 @@
|
||||
<data name="MagicIso_NRG_trailer_has_no_tracks" xml:space="preserve">
|
||||
<value>The NRG trailer embedded in the MagicISO UIF image has no tracks.</value>
|
||||
</data>
|
||||
<data name="Verifying_NDIF_image_CRC" xml:space="preserve">
|
||||
<value>[slateblue1]Verifying NDIF image CRC…[/]</value>
|
||||
</data>
|
||||
<data name="NDIF_CRC_mismatch_expected_0_got_1" xml:space="preserve">
|
||||
<value>NDIF image CRC mismatch: expected 0x{0:X7}, got 0x{1:X7}</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -38,12 +38,10 @@ using Aaru.CommonTypes.Structs;
|
||||
namespace Aaru.Images;
|
||||
|
||||
// TODO: Detect OS X encrypted images
|
||||
// TODO: Check checksum
|
||||
// TODO: Implement segments
|
||||
// TODO: Implement compression
|
||||
/// <inheritdoc />
|
||||
/// <inheritdoc cref="IMediaImage" />
|
||||
/// <summary>Implements reading Apple New Disk Image Format disk images</summary>
|
||||
public sealed partial class Ndif : IMediaImage
|
||||
public sealed partial class Ndif : IMediaImage, IVerifiableImage
|
||||
{
|
||||
const string MODULE_NAME = "NDIF plugin";
|
||||
uint _bufferSize;
|
||||
|
||||
104
Aaru.Images/NDIF/NdifCrc28.cs
Normal file
104
Aaru.Images/NDIF/NdifCrc28.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : NdifCrc28.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Disk image plugins.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Implements the 28-bit CRC used to protect Apple New Disk Image Format
|
||||
// images.
|
||||
//
|
||||
// Reverse-engineered from the 68k code fragment (`oneb` 128) embedded in
|
||||
// the Apple DiskCopy 6.3.3 resource fork (exports `.CRC28_Initialize`,
|
||||
// `.CRC28_Start`, `.CRC28_ProcessBuffer`, `.CRC28_Finish`):
|
||||
//
|
||||
// table[i] = i
|
||||
// for each of 8 bits: if (table[i] & 1)
|
||||
// table[i] = (table[i] >> 1) ^ 0x04C11DB7
|
||||
// else
|
||||
// table[i] >>= 1
|
||||
//
|
||||
// crc = 0xFFFFFFFF
|
||||
// for each byte b of the decoded sector stream:
|
||||
// crc = (crc >> 8) ^ table[(crc ^ b) & 0xFF]
|
||||
//
|
||||
// CRC28_Finish is a no-op; the low 28 bits of the accumulator are what the
|
||||
// NDIF `bcem` header stores in its `crc` field.
|
||||
//
|
||||
// Buffer contents are the reconstructed 512-byte sectors of the image, in
|
||||
// image order; `CHUNK_TYPE_NOCOPY` (and otherwise unwritten) sectors feed
|
||||
// in as zeros.
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as
|
||||
// published by the Free Software Foundation; either version 2.1 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2026 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
namespace Aaru.Images;
|
||||
|
||||
/// <summary>Table-driven implementation of Apple NDIF's 28-bit CRC.</summary>
|
||||
static class NdifCrc28
|
||||
{
|
||||
const uint POLYNOMIAL = 0x04C11DB7;
|
||||
const uint SEED = 0xFFFFFFFF;
|
||||
const uint MASK_28 = 0x0FFFFFFF;
|
||||
|
||||
static readonly uint[] _table = BuildTable();
|
||||
|
||||
static uint[] BuildTable()
|
||||
{
|
||||
var table = new uint[256];
|
||||
|
||||
for(uint i = 0; i < 256; i++)
|
||||
{
|
||||
uint c = i;
|
||||
|
||||
for(var j = 0; j < 8; j++)
|
||||
{
|
||||
if((c & 1) != 0)
|
||||
c = c >> 1 ^ POLYNOMIAL;
|
||||
else
|
||||
c >>= 1;
|
||||
}
|
||||
|
||||
table[i] = c;
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
/// <summary>Returns the initial CRC accumulator seed.</summary>
|
||||
public static uint Init() => SEED;
|
||||
|
||||
/// <summary>
|
||||
/// Feeds <paramref name="length" /> bytes from <paramref name="buffer" /> into the running
|
||||
/// <paramref name="crc" />.
|
||||
/// </summary>
|
||||
public static uint Update(uint crc, byte[] buffer, int length)
|
||||
{
|
||||
for(var i = 0; i < length; i++) crc = crc >> 8 ^ _table[(crc ^ buffer[i]) & 0xFF];
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
/// <summary>Applies the final 28-bit mask to <paramref name="crc" />, yielding the value stored in the NDIF header.</summary>
|
||||
public static uint Finish(uint crc) => crc & MASK_28;
|
||||
}
|
||||
105
Aaru.Images/NDIF/Verify.cs
Normal file
105
Aaru.Images/NDIF/Verify.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : Verify.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Disk image plugins.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Verifies the internal 28-bit CRC of an Apple New Disk Image Format
|
||||
// image.
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as
|
||||
// published by the Free Software Foundation; either version 2.1 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2026 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System.Linq;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.Logging;
|
||||
|
||||
namespace Aaru.Images;
|
||||
|
||||
public sealed partial class Ndif
|
||||
{
|
||||
#region IVerifiableImage Members
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the image against the 28-bit CRC stored in the NDIF <c>bcem</c> header. The CRC is computed over the
|
||||
/// reconstructed 512-byte-per-sector stream in image order; <c>CHUNK_TYPE_NOCOPY</c> sectors feed in as zeros,
|
||||
/// matching Apple DiskCopy's behaviour.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the recomputed CRC matches the one stored in the header; <c>false</c> otherwise; <c>null</c> when
|
||||
/// there are no chunks to verify.
|
||||
/// </returns>
|
||||
public bool? VerifyMediaImage()
|
||||
{
|
||||
if(_chunks is null || _chunks.Count == 0) return null;
|
||||
|
||||
AaruLogging.WriteLine(Localization.Verifying_NDIF_image_CRC);
|
||||
|
||||
var zeroSector = new byte[SECTOR_SIZE];
|
||||
uint crc = NdifCrc28.Init();
|
||||
|
||||
// NDIF BlockChunk does not carry an explicit sector count, so we derive it from the start of the next chunk (or
|
||||
// the image total for the last one) after sorting by starting sector.
|
||||
var ordered = _chunks.OrderBy(static kvp => kvp.Key).ToList();
|
||||
|
||||
for(var i = 0; i < ordered.Count; i++)
|
||||
{
|
||||
ulong start = ordered[i].Key;
|
||||
BlockChunk chunk = ordered[i].Value;
|
||||
|
||||
if(chunk.type == CHUNK_TYPE_END) continue;
|
||||
|
||||
ulong end = i + 1 < ordered.Count ? ordered[i + 1].Key : _imageInfo.Sectors;
|
||||
ulong chunkSectorCt = end - start;
|
||||
|
||||
if(chunk.type == CHUNK_TYPE_NOCOPY)
|
||||
{
|
||||
// Fast path: avoid going through ReadSector for empty regions.
|
||||
for(ulong s = 0; s < chunkSectorCt; s++) crc = NdifCrc28.Update(crc, zeroSector, (int)SECTOR_SIZE);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for(ulong s = 0; s < chunkSectorCt; s++)
|
||||
{
|
||||
ErrorNumber errno = ReadSector(start + s, false, out byte[] sector, out _);
|
||||
|
||||
if(errno != ErrorNumber.NoError || sector is null) return false;
|
||||
|
||||
crc = NdifCrc28.Update(crc, sector, (int)SECTOR_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
uint calculated = NdifCrc28.Finish(crc);
|
||||
uint stored = _header.crc & 0x0FFFFFFF;
|
||||
|
||||
if(calculated == stored) return true;
|
||||
|
||||
AaruLogging.WriteLine(Localization.NDIF_CRC_mismatch_expected_0_got_1, stored, calculated);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user