// /*************************************************************************** // Aaru Data Preservation Suite // ---------------------------------------------------------------------------- // // Filename : Checksum.cs // Author(s) : Natalia Portillo // // Component : Core algorithms. // // --[ Description ] ---------------------------------------------------------- // // Multithread checksumming and hashing. // // --[ License ] -------------------------------------------------------------- // // 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 the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program 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 this program. If not, see . // // ---------------------------------------------------------------------------- // Copyright © 2011-2025 Natalia Portillo // ****************************************************************************/ using System; using System.Collections.Generic; using System.Threading; using Aaru.Checksums; using Aaru.CommonTypes.AaruMetadata; using Aaru.CommonTypes.Interfaces; namespace Aaru.Core; /// Enabled checksums [Flags] public enum EnableChecksum { /// Enables Adler-32 Adler32 = 1, /// Enables CRC-16 Crc16 = 2, /// Enables CRC-32 Crc32 = 4, /// Enables CRC-64 Crc64 = 8, /// Enables MD5 Md5 = 16, /// Enables SHA1 Sha1 = 64, /// Enables SHA2-256 Sha256 = 128, /// Enables SHA2-384 Sha384 = 256, /// Enables SHA2-512 Sha512 = 512, /// Enables SpamSum SpamSum = 1024, /// Enables Fletcher-16 Fletcher16 = 2048, /// Enables Fletcher-32 Fletcher32 = 4096, /// Enables all known checksums All = Adler32 | Crc16 | Crc32 | Crc64 | Md5 | Sha1 | Sha256 | Sha384 | Sha512 | SpamSum | Fletcher16 | Fletcher32 } /// Checksums and hashes data, with different algorithms, multithreaded public sealed class Checksum { readonly IChecksum _adler32Ctx; readonly IChecksum _crc16Ctx; readonly IChecksum _crc32Ctx; readonly IChecksum _crc64Ctx; readonly EnableChecksum _enabled; readonly IChecksum _f16Ctx; readonly IChecksum _f32Ctx; readonly IChecksum _md5Ctx; readonly IChecksum _sha1Ctx; readonly IChecksum _sha256Ctx; readonly IChecksum _sha384Ctx; readonly IChecksum _sha512Ctx; readonly IChecksum _ssCtx; HashPacket _adlerPkt; Thread _adlerThread; HashPacket _crc16Pkt; Thread _crc16Thread; HashPacket _crc32Pkt; Thread _crc32Thread; HashPacket _crc64Pkt; Thread _crc64Thread; HashPacket _f16Pkt; Thread _f16Thread; HashPacket _f32Pkt; Thread _f32Thread; HashPacket _md5Pkt; Thread _md5Thread; HashPacket _sha1Pkt; Thread _sha1Thread; HashPacket _sha256Pkt; Thread _sha256Thread; HashPacket _sha384Pkt; Thread _sha384Thread; HashPacket _sha512Pkt; Thread _sha512Thread; HashPacket _spamsumPkt; Thread _spamsumThread; /// Initializes an instance of the checksum operations /// Enabled checksums public Checksum(EnableChecksum enabled = EnableChecksum.All) { _enabled = enabled; if(enabled.HasFlag(EnableChecksum.Adler32)) { _adler32Ctx = new Adler32Context(); _adlerPkt = new HashPacket { Context = _adler32Ctx }; } if(enabled.HasFlag(EnableChecksum.Crc16)) { _crc16Ctx = new CRC16IbmContext(); _crc16Pkt = new HashPacket { Context = _crc16Ctx }; } if(enabled.HasFlag(EnableChecksum.Crc32)) { _crc32Ctx = new Crc32Context(); _crc32Pkt = new HashPacket { Context = _crc32Ctx }; } if(enabled.HasFlag(EnableChecksum.Crc64)) { _crc64Ctx = new Crc64Context(); _crc64Pkt = new HashPacket { Context = _crc64Ctx }; } if(enabled.HasFlag(EnableChecksum.Md5)) { _md5Ctx = new Md5Context(); _md5Pkt = new HashPacket { Context = _md5Ctx }; } if(enabled.HasFlag(EnableChecksum.Sha1)) { _sha1Ctx = new Sha1Context(); _sha1Pkt = new HashPacket { Context = _sha1Ctx }; } if(enabled.HasFlag(EnableChecksum.Sha256)) { _sha256Ctx = new Sha256Context(); _sha256Pkt = new HashPacket { Context = _sha256Ctx }; } if(enabled.HasFlag(EnableChecksum.Sha384)) { _sha384Ctx = new Sha384Context(); _sha384Pkt = new HashPacket { Context = _sha384Ctx }; } if(enabled.HasFlag(EnableChecksum.Sha512)) { _sha512Ctx = new Sha512Context(); _sha512Pkt = new HashPacket { Context = _sha512Ctx }; } if(enabled.HasFlag(EnableChecksum.SpamSum)) { _ssCtx = new SpamSumContext(); _spamsumPkt = new HashPacket { Context = _ssCtx }; } if(enabled.HasFlag(EnableChecksum.Fletcher16)) { _f16Ctx = new Fletcher16Context(); _f16Pkt = new HashPacket { Context = _f16Ctx }; } if(enabled.HasFlag(EnableChecksum.Fletcher32)) { _f32Ctx = new Fletcher32Context(); _f32Pkt = new HashPacket { Context = _f32Ctx }; } _adlerThread = new Thread(UpdateHash); _crc16Thread = new Thread(UpdateHash); _crc32Thread = new Thread(UpdateHash); _crc64Thread = new Thread(UpdateHash); _md5Thread = new Thread(UpdateHash); _sha1Thread = new Thread(UpdateHash); _sha256Thread = new Thread(UpdateHash); _sha384Thread = new Thread(UpdateHash); _sha512Thread = new Thread(UpdateHash); _spamsumThread = new Thread(UpdateHash); _f16Thread = new Thread(UpdateHash); _f32Thread = new Thread(UpdateHash); } /// Updates the checksum with new data /// New data public void Update(byte[] data) { if(_enabled.HasFlag(EnableChecksum.Adler32)) { _adlerPkt.Data = data; _adlerThread.Start(_adlerPkt); } if(_enabled.HasFlag(EnableChecksum.Crc16)) { _crc16Pkt.Data = data; _crc16Thread.Start(_crc16Pkt); } if(_enabled.HasFlag(EnableChecksum.Crc32)) { _crc32Pkt.Data = data; _crc32Thread.Start(_crc32Pkt); } if(_enabled.HasFlag(EnableChecksum.Crc64)) { _crc64Pkt.Data = data; _crc64Thread.Start(_crc64Pkt); } if(_enabled.HasFlag(EnableChecksum.Md5)) { _md5Pkt.Data = data; _md5Thread.Start(_md5Pkt); } if(_enabled.HasFlag(EnableChecksum.Sha1)) { _sha1Pkt.Data = data; _sha1Thread.Start(_sha1Pkt); } if(_enabled.HasFlag(EnableChecksum.Sha256)) { _sha256Pkt.Data = data; _sha256Thread.Start(_sha256Pkt); } if(_enabled.HasFlag(EnableChecksum.Sha384)) { _sha384Pkt.Data = data; _sha384Thread.Start(_sha384Pkt); } if(_enabled.HasFlag(EnableChecksum.Sha512)) { _sha512Pkt.Data = data; _sha512Thread.Start(_sha512Pkt); } if(_enabled.HasFlag(EnableChecksum.SpamSum)) { _spamsumPkt.Data = data; _spamsumThread.Start(_spamsumPkt); } if(_enabled.HasFlag(EnableChecksum.Fletcher16)) { _f16Pkt.Data = data; _f16Thread.Start(_f16Pkt); } if(_enabled.HasFlag(EnableChecksum.Fletcher32)) { _f32Pkt.Data = data; _f32Thread.Start(_f32Pkt); } while(_adlerThread.IsAlive || _crc16Thread.IsAlive || _crc32Thread.IsAlive || _crc64Thread.IsAlive || _md5Thread.IsAlive || _sha1Thread.IsAlive || _sha256Thread.IsAlive || _sha384Thread.IsAlive || _sha512Thread.IsAlive || _spamsumThread.IsAlive || _f16Thread.IsAlive || _f32Thread.IsAlive) {} if(_enabled.HasFlag(EnableChecksum.Adler32)) _adlerThread = new Thread(UpdateHash); if(_enabled.HasFlag(EnableChecksum.Crc16)) _crc16Thread = new Thread(UpdateHash); if(_enabled.HasFlag(EnableChecksum.Crc32)) _crc32Thread = new Thread(UpdateHash); if(_enabled.HasFlag(EnableChecksum.Crc16)) _crc64Thread = new Thread(UpdateHash); if(_enabled.HasFlag(EnableChecksum.Md5)) _md5Thread = new Thread(UpdateHash); if(_enabled.HasFlag(EnableChecksum.Sha1)) _sha1Thread = new Thread(UpdateHash); if(_enabled.HasFlag(EnableChecksum.Sha256)) _sha256Thread = new Thread(UpdateHash); if(_enabled.HasFlag(EnableChecksum.Sha384)) _sha384Thread = new Thread(UpdateHash); if(_enabled.HasFlag(EnableChecksum.Sha512)) _sha512Thread = new Thread(UpdateHash); if(_enabled.HasFlag(EnableChecksum.SpamSum)) _spamsumThread = new Thread(UpdateHash); if(_enabled.HasFlag(EnableChecksum.Fletcher16)) _f16Thread = new Thread(UpdateHash); if(_enabled.HasFlag(EnableChecksum.Fletcher32)) _f32Thread = new Thread(UpdateHash); } /// Finishes the checksums /// Returns the checksum results public List End() { List chks = []; if(_enabled.HasFlag(EnableChecksum.Adler32)) { chks.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.Adler32, Value = _adler32Ctx.End() }); } if(_enabled.HasFlag(EnableChecksum.Crc16)) { chks.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.CRC16, Value = _crc16Ctx.End() }); } if(_enabled.HasFlag(EnableChecksum.Crc32)) { chks.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.CRC32, Value = _crc32Ctx.End() }); } if(_enabled.HasFlag(EnableChecksum.Crc64)) { chks.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.CRC64, Value = _crc64Ctx.End() }); } if(_enabled.HasFlag(EnableChecksum.Md5)) { chks.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.Md5, Value = _md5Ctx.End() }); } if(_enabled.HasFlag(EnableChecksum.Sha1)) { chks.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.Sha1, Value = _sha1Ctx.End() }); } if(_enabled.HasFlag(EnableChecksum.Sha256)) { chks.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.Sha256, Value = _sha256Ctx.End() }); } if(_enabled.HasFlag(EnableChecksum.Sha384)) { chks.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.Sha384, Value = _sha384Ctx.End() }); } if(_enabled.HasFlag(EnableChecksum.Sha512)) { chks.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.Sha512, Value = _sha512Ctx.End() }); } if(_enabled.HasFlag(EnableChecksum.SpamSum)) { chks.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.SpamSum, Value = _ssCtx.End() }); } if(_enabled.HasFlag(EnableChecksum.Fletcher16)) { chks.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.Fletcher16, Value = _f16Ctx.End() }); } if(!_enabled.HasFlag(EnableChecksum.Fletcher32)) return chks; chks.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.Fletcher32, Value = _f32Ctx.End() }); return chks; } internal static List GetChecksums( byte[] data, EnableChecksum enabled = EnableChecksum.All) { IChecksum adler32CtxData = null; IChecksum crc16CtxData = null; IChecksum crc32CtxData = null; IChecksum crc64CtxData = null; IChecksum md5CtxData = null; IChecksum sha1CtxData = null; IChecksum sha256CtxData = null; IChecksum sha384CtxData = null; IChecksum sha512CtxData = null; IChecksum ssctxData = null; IChecksum f16CtxData = null; IChecksum f32CtxData = null; var adlerThreadData = new Thread(UpdateHash); var crc16ThreadData = new Thread(UpdateHash); var crc32ThreadData = new Thread(UpdateHash); var crc64ThreadData = new Thread(UpdateHash); var md5ThreadData = new Thread(UpdateHash); var sha1ThreadData = new Thread(UpdateHash); var sha256ThreadData = new Thread(UpdateHash); var sha384ThreadData = new Thread(UpdateHash); var sha512ThreadData = new Thread(UpdateHash); var spamsumThreadData = new Thread(UpdateHash); var f16ThreadData = new Thread(UpdateHash); var f32ThreadData = new Thread(UpdateHash); if(enabled.HasFlag(EnableChecksum.Adler32)) { adler32CtxData = new Adler32Context(); var adlerPktData = new HashPacket { Context = adler32CtxData, Data = data }; adlerThreadData.Start(adlerPktData); } if(enabled.HasFlag(EnableChecksum.Crc16)) { crc16CtxData = new CRC16IbmContext(); var crc16PktData = new HashPacket { Context = crc16CtxData, Data = data }; crc16ThreadData.Start(crc16PktData); } if(enabled.HasFlag(EnableChecksum.Crc32)) { crc32CtxData = new Crc32Context(); var crc32PktData = new HashPacket { Context = crc32CtxData, Data = data }; crc32ThreadData.Start(crc32PktData); } if(enabled.HasFlag(EnableChecksum.Crc64)) { crc64CtxData = new Crc64Context(); var crc64PktData = new HashPacket { Context = crc64CtxData, Data = data }; crc64ThreadData.Start(crc64PktData); } if(enabled.HasFlag(EnableChecksum.Md5)) { md5CtxData = new Md5Context(); var md5PktData = new HashPacket { Context = md5CtxData, Data = data }; md5ThreadData.Start(md5PktData); } if(enabled.HasFlag(EnableChecksum.Sha1)) { sha1CtxData = new Sha1Context(); var sha1PktData = new HashPacket { Context = sha1CtxData, Data = data }; sha1ThreadData.Start(sha1PktData); } if(enabled.HasFlag(EnableChecksum.Sha256)) { sha256CtxData = new Sha256Context(); var sha256PktData = new HashPacket { Context = sha256CtxData, Data = data }; sha256ThreadData.Start(sha256PktData); } if(enabled.HasFlag(EnableChecksum.Sha384)) { sha384CtxData = new Sha384Context(); var sha384PktData = new HashPacket { Context = sha384CtxData, Data = data }; sha384ThreadData.Start(sha384PktData); } if(enabled.HasFlag(EnableChecksum.Sha512)) { sha512CtxData = new Sha512Context(); var sha512PktData = new HashPacket { Context = sha512CtxData, Data = data }; sha512ThreadData.Start(sha512PktData); } if(enabled.HasFlag(EnableChecksum.SpamSum)) { ssctxData = new SpamSumContext(); var spamsumPktData = new HashPacket { Context = ssctxData, Data = data }; spamsumThreadData.Start(spamsumPktData); } if(enabled.HasFlag(EnableChecksum.Fletcher16)) { f16CtxData = new Fletcher16Context(); var f16PktData = new HashPacket { Context = f16CtxData, Data = data }; f16ThreadData.Start(f16PktData); } if(enabled.HasFlag(EnableChecksum.Fletcher32)) { f32CtxData = new Fletcher32Context(); var f32PktData = new HashPacket { Context = f32CtxData, Data = data }; f32ThreadData.Start(f32PktData); } while(adlerThreadData.IsAlive || crc16ThreadData.IsAlive || crc32ThreadData.IsAlive || crc64ThreadData.IsAlive || md5ThreadData.IsAlive || sha1ThreadData.IsAlive || sha256ThreadData.IsAlive || sha384ThreadData.IsAlive || sha512ThreadData.IsAlive || spamsumThreadData.IsAlive || f16ThreadData.IsAlive || f32ThreadData.IsAlive) {} List dataChecksums = []; if(enabled.HasFlag(EnableChecksum.Adler32)) { dataChecksums.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.Adler32, Value = adler32CtxData.End() }); } if(enabled.HasFlag(EnableChecksum.Crc16)) { dataChecksums.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.CRC16, Value = crc16CtxData.End() }); } if(enabled.HasFlag(EnableChecksum.Crc32)) { dataChecksums.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.CRC32, Value = crc32CtxData.End() }); } if(enabled.HasFlag(EnableChecksum.Crc64)) { dataChecksums.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.CRC64, Value = crc64CtxData.End() }); } if(enabled.HasFlag(EnableChecksum.Md5)) { dataChecksums.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.Md5, Value = md5CtxData.End() }); } if(enabled.HasFlag(EnableChecksum.Sha1)) { dataChecksums.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.Sha1, Value = sha1CtxData.End() }); } if(enabled.HasFlag(EnableChecksum.Sha256)) { dataChecksums.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.Sha256, Value = sha256CtxData.End() }); } if(enabled.HasFlag(EnableChecksum.Sha384)) { dataChecksums.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.Sha384, Value = sha384CtxData.End() }); } if(enabled.HasFlag(EnableChecksum.Sha512)) { dataChecksums.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.Sha512, Value = sha512CtxData.End() }); } if(enabled.HasFlag(EnableChecksum.SpamSum)) { dataChecksums.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.SpamSum, Value = ssctxData.End() }); } if(enabled.HasFlag(EnableChecksum.Fletcher16)) { dataChecksums.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.Fletcher16, Value = f16CtxData.End() }); } if(!enabled.HasFlag(EnableChecksum.Fletcher32)) return dataChecksums; dataChecksums.Add(new CommonTypes.AaruMetadata.Checksum { Type = ChecksumType.Fletcher32, Value = f32CtxData.End() }); return dataChecksums; } #region Threading helpers struct HashPacket { public IChecksum Context; public byte[] Data; } static void UpdateHash(object packet) => ((HashPacket)packet).Context.Update(((HashPacket)packet).Data); #endregion Threading helpers }