private uint GetChecksum(byte[] data) { int start = Offset + 4; int end = start + Length - 4; return(Checksums.CheckSum16BigInvert(data, start, end)); }
public static ulong StrToU64(string input, out bool valid) { var chk = Pull(0, 4) >> 4; // first four chars are checksum bits var result = Pull(4, input.Length); // next 12 chars are the 70 value bits Span <byte> temp = stackalloc byte[8]; WriteUInt64LittleEndian(temp, result); var actual = Checksums.CRC16_CCITT(temp); valid = chk == actual; return(result); ulong Pull(int start, int count) { ulong val = 0; for (int i = start; i < count; i++) { var c = input[i]; if (c == '-') { continue; } val <<= 5; val |= Get5BitFromChar(c) & 0b11111; } return(val); } }
private static bool compareSaveImages(byte[] newChecksum, out Checksums sum) { sum = new Checksums(); for (int i = 0; i < _checkSums.Count; ++i) { byte[] cmp = _checkSums[i].checksum; if (((cmp == null) || (newChecksum == null)) || (cmp.Length != newChecksum.Length)) { return(false); } bool valid = true; for (int j = 0; j < cmp.Length; ++j) { if (cmp[j] == newChecksum[j]) { continue; } valid = false; break; } if (!valid) { continue; } sum = _checkSums[i]; return(true); } return(false); }
private static bool CompareSaveImages(IReadOnlyList <Checksums> checksumList, IReadOnlyList <byte> newChecksum, out Checksums sum) { sum = new Checksums(); for (int i = 0; i < checksumList.Count; ++i) { byte[] cmp = checksumList[i].checksum; if ((cmp == null) || (newChecksum == null) || (cmp.Length != newChecksum.Count)) { return(false); } bool valid = true; for (int j = 0; j < cmp.Length; ++j) { if (cmp[j] == newChecksum[j]) { continue; } valid = false; break; } if (!valid) { continue; } sum = checksumList[i]; return(true); } return(false); }
public ActionResult Index() { //Make list so files can be shown on frontend var model = new List <FileHandler>(); ClientFolder.CreateFolder(path); ClientFolder.CreateFolderChecksum(path, folderChecksum); // Set c so folder can be checked by client DirectoryInfo c = new DirectoryInfo(path); //Get all files FileInfo[] Files2 = c.GetFiles("*.*"); foreach (FileInfo file in Files2) { FileHandler tempFile = new FileHandler(); tempFile.FileName = file.Name; string filePath = c + file.Name; //Show SHA1 hash of current version of the file tempFile.Checksum = Checksums.GetSha1Hash(filePath); model.Add(tempFile); } return(View(model)); }
protected override void SetBoxChecksum(int box) { var boxOfs = GetBoxOffset(box) - ListHeaderSize; var size = BoxSize - 2; var chk = Checksums.CheckSum16(new ReadOnlySpan <byte>(Data, boxOfs, size)); WriteUInt16BigEndian(Data.AsSpan(boxOfs + size), chk); }
protected override bool GetIsBoxChecksumValid(int box) { var boxOfs = GetBoxOffset(box) - ListHeaderSize; var size = BoxSize - 2; var chk = Checksums.CheckSum16(new ReadOnlySpan <byte>(Data, boxOfs, size)); var actual = ReadUInt16BigEndian(Data.AsSpan(boxOfs + size)); return(chk == actual); }
/// <summary> /// 计算校验和 /// </summary> /// <param name="buffer">字节数组</param> /// <param name="checksum">使用校验类类型</param> /// <returns>校验和</returns> public long Checksum(byte[] buffer, Checksums checksum) { Guard.Requires <ArgumentNullException>(buffer != null); lock (syncRoot) { var checksumClass = GetChecksum(checksum); checksumClass.Update(buffer); return(checksumClass.Value); } }
/// <summary> /// Determines whether or not the given file is a save file for Pokémon Mystery Dungeon: Explorers of Time and Darkness. /// </summary> /// <param name="file">The file to be checked</param> /// <returns>A boolean indicating whether or not the given file is supported by this class</returns> public virtual async Task <bool> IsOfType(GenericFile file) { if (file.Length > Offsets.ChecksumEnd) { return(await file.ReadUInt32Async(0) == Checksums.Calculate32BitChecksum(file, 4, Offsets.ChecksumEnd)); } else { return(false); } }
/// <summary> /// 哈希 /// </summary> public Hashing(Checksums defaultChecksum, Encoding defaultEncoding) { Guard.Requires <ArgumentNullException>(defaultChecksum != null); Guard.Requires <ArgumentNullException>(defaultEncoding != null); this.defaultChecksum = defaultChecksum; this.defaultEncoding = defaultEncoding; checksumsMaker = new Dictionary <Checksums, Func <IChecksum> >(); checksumsDict = new Dictionary <Checksums, IChecksum>(); }
/// <summary> /// 哈希 /// </summary> public Hashing(Checksums defaultChecksum, Hashes defaultHash) { Guard.Requires <ArgumentNullException>(defaultChecksum != null); Guard.Requires <ArgumentNullException>(defaultHash != null); this.defaultChecksum = defaultChecksum; this.defaultHash = defaultHash; checksumsMaker = new Dictionary <Checksums, Func <IChecksum> >(); hashByteMaker = new Dictionary <Hashes, Func <HashAlgorithm> >(); checksumsDict = new Dictionary <Checksums, IChecksum>(); hashByteDict = new Dictionary <Hashes, HashAlgorithm>(); }
/// <summary> /// 将字节数组添加到数据校验和 /// </summary> /// <param name="callback">回调闭包</param> /// <param name="checksum">使用校验类类型</param> /// <returns></returns> public long Checksum(Action <Action <byte[], int, int> > callback, Checksums checksum) { Guard.Requires <ArgumentNullException>(callback != null); lock (syncRoot) { var checksumClass = GetChecksum(checksum); long value = 0; callback.Invoke((buffer, offset, count) => { value = Checksum(buffer, offset, count, checksumClass); }); return(value); } }
private static string GetGBPKM(GBPKM gb) { string form = gb.Form > 0 ? $"-{gb.Form:00}" : string.Empty; string star = gb.IsShiny ? " ★" : string.Empty; var raw = gb switch { PK1 pk1 => new PokeList1(pk1).Write(), PK2 pk2 => new PokeList2(pk2).Write(), _ => gb.Data, }; var checksum = Checksums.CRC16_CCITT(raw); return($"{gb.Species:000}{form}{star} - {gb.Nickname} - {checksum:X4}"); }
private void B_Save_Click(object sender, EventArgs e) { byte[] data = new byte[SAV.LinkBlock.Length]; Array.Copy(LinkInfo.Data, 0, data, 0x1FF, LinkInfo.Data.Length); // Fix Checksum just in case. ushort ccitt = Checksums.CRC16_CCITT(data, 0x200, data.Length - 4 - 0x200); // [app,chk) BitConverter.GetBytes(ccitt).CopyTo(data, data.Length - 4); SAV.LinkBlock = data; Origin.SetData(((SaveFile)SAV).Data, 0); Close(); }
private void AddFiles(IList <string> paths) { var elements = new List <HashSignatureElement>(); foreach (string path in paths) { var file = new FileItem(new FileInfo(path)); string sum = Checksums.GetChecksumFromFile(file, Checksums.ChecksumType.SHA1); elements.Add(new HashSignatureElement(file, sum)); } files = files.Union(elements, new HashSignatureElementComparer()).ToList(); modified = true; }
public (bool detected, Detection detection) Scan(FileItem file) { var elementsMatchingSize = new List <HashSignatureElement>(); string sha1 = string.Empty; if (this.Status == DetectorStatus.UNUSED) { logger.Error("{0} detector in use when it should have been pruned", this.Name); return(false, null); } if (this.Status == DetectorStatus.UNINITIALIZED) { string message = string.Format( "Trying to use the {0} detector while it is uninitialized", this.Name); logger.Error(message); throw new Exception(message); } /* First check if any elements match the size of the file being scanned */ foreach (var element in elements) { if (element.Length == file.Length) { logger.Trace("Size match: {0} matches size of {1}", file.AbsolutePath, element.Name); elementsMatchingSize.Add(element); } } if (elementsMatchingSize.Count > 0) { sha1 = Checksums.GetChecksumFromFile(file, Checksums.ChecksumType.SHA1); foreach (var element in elements) { if (sha1.Equals(element.SHA1)) { logger.Trace("Exact match: {0} matches hash of {1}", file.AbsolutePath, element.Name); logger.Trace("Matching SHA1 sum was {0}", sha1); return(true, new Detection(this.Type, this.Name, string.Format("Hash matches {0}", element.Name))); } } } return(false, null); }
/// <summary> /// 获取校验器 /// </summary> /// <param name="checksum">校验器类型</param> /// <returns>校验器</returns> private IChecksum GetChecksum(Checksums checksum) { IChecksum checksumClass; if (!checksumsDict.TryGetValue(checksum, out checksumClass)) { Func <IChecksum> checksumMaker; if (!checksumsMaker.TryGetValue(checksum, out checksumMaker) || (checksumClass = checksumMaker.Invoke()) == null) { throw new RuntimeException("Undefiend Checksum:" + checksum); } checksumsDict[checksum] = checksumClass; } checksumClass.Reset(); return(checksumClass); }
/// <summary> /// Initializes the packet from a stream through a <see cref="Stream"/>. /// </summary> /// <param name="stream">A <see cref="Stream"/> containing the packet.</param> protected override void Initialize(Stream stream) { using (var reader = new BinaryReader(stream, Encoding.ASCII, true)) { FileID = new FileID { ID = reader.ReadBytes(16) }; while (reader.BaseStream.Position < reader.BaseStream.Length) { Checksums.Add(new InputFileSliceChecksum { MD5 = reader.ReadBytes(16), CRC32 = reader.ReadUInt32() }); } } }
private void SaveBattleFrontier() { if (ofsPrints > 0) { for (int i = 0; i < Prints.Length; i++) { if (Prints[i] == 1 + Math.Sign((BitConverter.ToUInt16(SAV.General, ofsPrints + (i << 1)) >> 1) - 1)) { continue; } BitConverter.GetBytes(Prints[i] << 1).CopyTo(SAV.General, ofsPrints + (i << 1)); } } if (HallStatUpdated) { BitConverter.GetBytes(Checksums.CRC16_CCITT(SAV.Data, ofsHallStat, 0xBAE)).CopyTo(SAV.Data, ofsHallStat + 0xBAE); } }
private void AddFiles(IList <string> paths) { var elements = new List <SignatureElement>(); foreach (string path in paths) { var info = new FileInfo(path); var file = new FileItem(info) { Owner = Paths.GetOwner(info) }; string sum = Checksums.GetChecksumFromFile(file, Checksums.ChecksumType.SHA1); elements.Add(new HashSignatureElement(file, sum)); } task.Elements = task.Elements.Union(elements, new SignatureElementComparer()).ToList(); }
/// <summary> /// 计算校验和 /// </summary> /// <param name="buffer">字节数组</param> /// <param name="checksum">使用校验类类型</param> /// <returns>校验和</returns> public long Checksum(byte[] buffer, Checksums checksum) { Guard.Requires <ArgumentNullException>(buffer != null); IChecksum checksumClass; if (!checksumsDict.TryGetValue(checksum, out checksumClass)) { Func <IChecksum> checksumMaker; if (!checksumsMaker.TryGetValue(checksum, out checksumMaker) || (checksumClass = checksumMaker.Invoke()) == null) { throw new RuntimeException("Undefiend Checksum:" + checksum); } checksumsDict[checksum] = checksumClass; } lock (syncRoot) { checksumClass.Reset(); checksumClass.Update(buffer); return(checksumClass.Value); } }
public static string U64ToStr(ulong input, bool insertDash) { Span <byte> temp = stackalloc byte[8]; WriteUInt64LittleEndian(temp, input); uint chk = Checksums.CRC16_CCITT(temp); var buff = new char[16]; int ctr = 15; Push(input, 12); // store value bits Push(chk << 4, 4); // store checksum bits return(!insertDash?string.Concat(buff) : GetStringWithDashesEvery(buff, 4)); void Push(ulong v, int count) { for (int i = 0; i < count; i++) { buff[ctr--] = Set5BitToChar((char)(v & 0b11111)); v >>= 5; } } }
/// <summary> /// Decrypts the selected file using the given password /// </summary> /// <param name="inputFile">Encrypted File</param> /// <param name="password">Password to decrypt the file</param> /// <param name="percentComplete">Percent of completion</param> /// <returns>If the decryption was successful</returns> internal bool Decrypt(string inputFile, string password, ref decimal percentComplete) { byte[] passwordBytes = Encoding.UTF8.GetBytes(password); FileStream fsCrypt = new FileStream(inputFile, FileMode.Open); fsCrypt = DecryptModeHandler(fsCrypt, out byte[] hash, out byte[] salt, out byte[] faesCBCMode, out byte[] faesMetaData, out var cipher); const int keySize = 256; const int blockSize = 128; Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(passwordBytes, salt, 50000); RijndaelManaged AES = new RijndaelManaged { KeySize = keySize, BlockSize = blockSize, Key = key.GetBytes(keySize / 8), IV = key.GetBytes(blockSize / 8), Padding = PaddingMode.PKCS7, Mode = cipher }; try { CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateDecryptor(), CryptoStreamMode.Read); string outputName = Path.ChangeExtension(inputFile, FileAES_Utilities.ExtentionUFAES); try { FileStream fsOut = new FileStream(outputName, FileMode.Create); File.SetAttributes(outputName, FileAttributes.Hidden); byte[] buffer = new byte[FileAES_Utilities.GetCryptoStreamBuffer()]; long expectedComplete = fsCrypt.Length + hash.Length + salt.Length + faesCBCMode.Length + faesMetaData.Length + AES.KeySize + AES.BlockSize; try { int read; Logging.Log("Beginning writing decrypted data...", Severity.DEBUG); while ((read = cs.Read(buffer, 0, buffer.Length)) > 0) { try { percentComplete = Math.Ceiling((decimal)((Convert.ToDouble(fsOut.Length) / Convert.ToDouble(expectedComplete)) * 100)); if (percentComplete > 100) { percentComplete = 100; } } catch { Logging.Log("Percentage completion calculation failed!", Severity.WARN); } fsOut.Write(buffer, 0, read); } Logging.Log("Finished writing decrypted data.", Severity.DEBUG); } catch { fsOut.Close(); } cs.Close(); fsOut.Close(); fsCrypt.Close(); if (Checksums.ConvertHashToString(hash) != Checksums.ConvertHashToString(Checksums.GetSHA1(outputName))) { Logging.Log("Invalid Checksum detected! Assuming password is incorrect.", Severity.DEBUG); FileAES_IntUtilities.SafeDeleteFile(outputName); return(false); } Logging.Log("Valid Checksum detected!", Severity.DEBUG); return(true); } catch { cs.Close(); fsCrypt.Close(); return(false); } } catch (CryptographicException) { fsCrypt.Close(); return(false); } }
/// <summary> /// Decrypts the selected file using the given password /// </summary> /// <param name="faesMetaData">Formatted Metadata used at the start of a file</param> /// <param name="inputFilePath">File path for encrypted file</param> /// <param name="outputFilePath">File path for unencrypted file</param> /// <param name="encryptionPassword">Encryption Password</param> /// <param name="percentComplete">Percent completion of the encryption process</param> /// <returns>If the decryption was successful</returns> internal bool Decrypt(MetaData faesMetaData, string inputFilePath, string outputFilePath, string encryptionPassword, ref decimal percentComplete) { CipherMode cipher = CipherMode.CBC; byte[] metaData = new byte[faesMetaData.GetLength()]; byte[] salt = new byte[32]; byte[] passwordBytes = Encoding.UTF8.GetBytes(encryptionPassword); FileStream inputDataStream = new FileStream(inputFilePath, FileMode.Open); inputDataStream.Read(metaData, 0, faesMetaData.GetLength()); inputDataStream.Read(salt, 0, salt.Length); const int keySize = 256; const int blockSize = 128; Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(passwordBytes, salt, 51200); RijndaelManaged AES = new RijndaelManaged { KeySize = blockSize, BlockSize = 128, Key = key.GetBytes(keySize / 8), IV = key.GetBytes(blockSize / 8), Padding = PaddingMode.PKCS7, Mode = cipher }; try { CryptoStream crypto = new CryptoStream(inputDataStream, AES.CreateDecryptor(), CryptoStreamMode.Read); FileStream outputDataStream = new FileStream(outputFilePath, FileMode.Create); try { byte[] buffer = new byte[FileAES_Utilities.GetCryptoStreamBuffer()]; long expectedComplete = salt.Length + AES.KeySize + AES.BlockSize; try { Logging.Log("Beginning writing decrypted data...", Severity.DEBUG); int read; while ((read = crypto.Read(buffer, 0, buffer.Length)) > 0) { try { percentComplete = Math.Ceiling((decimal)((Convert.ToDouble(outputDataStream.Length) / Convert.ToDouble(expectedComplete)) * 100)); if (percentComplete > 100) { percentComplete = 100; } } catch { // ignored } outputDataStream.Write(buffer, 0, read); } Logging.Log("Finished writing decrypted data.", Severity.DEBUG); } catch { outputDataStream.Close(); } crypto.Close(); outputDataStream.Close(); inputDataStream.Close(); bool doesHashMatch = false; switch (faesMetaData.GetHashType()) { case Checksums.ChecksumType.SHA1: doesHashMatch = Checksums.CompareHash(faesMetaData.GetOrigHash(), Checksums.GetSHA1(outputFilePath)); break; case Checksums.ChecksumType.SHA256: doesHashMatch = Checksums.CompareHash(faesMetaData.GetOrigHash(), Checksums.GetSHA256(outputFilePath)); break; case Checksums.ChecksumType.SHA512: doesHashMatch = Checksums.CompareHash(faesMetaData.GetOrigHash(), Checksums.GetSHA512(outputFilePath)); break; case Checksums.ChecksumType.SHA384: doesHashMatch = Checksums.CompareHash(faesMetaData.GetOrigHash(), Checksums.GetSHA384(outputFilePath)); break; } if (!doesHashMatch) { Logging.Log("Invalid Checksum detected! Assuming password is incorrect.", Severity.DEBUG); return(false); } Logging.Log("Valid Checksum detected!", Severity.DEBUG); return(true); } catch { crypto.Close(); inputDataStream.Close(); outputDataStream.Close(); return(false); } } catch (CryptographicException) { inputDataStream.Close(); return(false); } }
/// <summary> /// A function to upload an INI file to a unit for quick configuration. /// </summary> /// <param name="stream">The stream to upload to the unit.</param> /// <param name="message">A string containing any status messages from the uploader.</param> /// <param name="timeoutSeconds">Number of seconds to try the upload before canceling.</param> /// <returns>True if stream was uploaded successfully, false otherwise.</returns> public bool UploadIni(Stream stream, out string message, int timeoutSeconds = 5) { message = ""; // make a local copy of the stream to upload var workingFile = new List <byte>(); var payload = new List <byte>(); workingFile.AddRange(stream.ToByteArray()); var timeout = DateTime.Now.AddSeconds(timeoutSeconds); var pointer = 0; ushort page = 0; ushort missedPage = 0; var missedPageCount = 0; var retransmit = false; const string command = "&@u"; while (true) { if (timeout < DateTime.Now) { message = "Download Timeout!"; return(false); } var payloadLength = Math.Min(workingFile.Count - pointer, 1024); if (!retransmit) { // load the array into a list with page information payload.Clear(); payload.AddRange(DataConversions.ConvertUInt16ToList(page)); payload.AddRange(workingFile.GetRange(pointer, payloadLength)); payload.AddRange(Checksums.Fletcher16(payload)); // add escapes to the payload payload.EscapeList(); // add the command to the front of the payload payload.InsertRange(0, Encoding.ASCII.GetBytes(command)); } // send the data retransmit = false; var returnString = _port.SendCommand(payload, 1).FirstOrDefault(); if (returnString?.Contains($"{command}{page},{payloadLength}") == true) { // housekeeping variables pointer += payloadLength; page++; // we had a good transfer, determine next step if (pointer == workingFile.Count) { // we are at the end of the file, so tell the system we are done payload.Clear(); payload.AddRange(DataConversions.ConvertUInt16ToList(0xFFFF)); payload.AddRange(Checksums.Fletcher16(payload)); // add escapes to the payload payload.EscapeList(); // add the command to the front of the payload payload.InsertRange(0, Encoding.ASCII.GetBytes(command)); // send command var returnStrings = _port.SendCommand(payload, 5000); if (returnStrings.Count > 0) { if (returnStrings[0].Contains("&@us")) { // upload successfull message = "INI Upload Successfull!"; return(true); } if (returnStrings[0].Contains("&@ue")) { // file error returnStrings[0] = returnStrings[0].Replace("&@ue", ""); returnStrings.RemoveRange(returnStrings.Count - 2, 2); var substring = string.Join("\r\n", returnStrings.ToArray()); message = $"INI Upload Failed! The INI file had the following errors:{Environment.NewLine}{substring}"; return(false); } } else { message = "Unable to parse INI file, unknown error!"; return(false); } } // next loop continue; } // switch to deal with packet error types switch (returnString) { case "&@u!c": message = "Checksum Error!"; break; case "&@u!w": message = "Data processing error!"; break; case "&@u!s": message = "Upload Complete!"; break; case "&@u!e": message = "Upload Error!"; break; case "": message = "Lost Connection!"; break; default: break; } // process the missed packet count if (page == missedPage) { missedPageCount++; if (missedPageCount > 5) { // upload failed return(false); } } else { // missed a different page, so reset counter missedPage = page; missedPageCount = 1; } // retransmit page retransmit = true; } }
private ushort GetChecksum(ReadOnlySpan <byte> data) => Checksums.CRC16_CCITT(data.Slice(Offset, Length));
public bool BuildCPK(string buildFile) { if (!DeserializeFromDisk(buildFile)) { return(false); } List <CPKEmbeddedFileMeta> changedFiles = new List <CPKEmbeddedFileMeta>(); foreach (var file in files.Values) { string filePath = Path.Combine(ProjectFolder.GetRootDir(), file.filePath); if (!File.Exists(filePath)) { string errorMessage = string.Format("File {0} did not exist.", filePath); MessageBox.Show(errorMessage, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return(false); } FileStream fs = new FileStream(filePath, FileMode.Open); BinaryReader br = new BinaryReader(fs); byte[] fileAsBytes = br.ReadBytes((int)br.BaseStream.Length); br.Close(); fs.Close(); string checksum = Checksums.GetMD5(fileAsBytes); if (checksum != file.checksumValue) // file has been changed { changedFiles.Add(file); } } string originalCPKPath = Path.Combine(ProjectFolder.GetRootDir(), originalFileLocation); string targetCPKPath = Path.Combine(ProjectFolder.GetRootDir(), targetFileLocation); if (DebugSettings.REFRESH_REPACKED_FILES_ON_BUILD) { if (File.Exists(targetCPKPath)) // refresh files in repacked files folder just in case since 1) we will be using the repacked folder as our base to merge new changes into 2) a rebuild implies a refresh. perhaps some distinction between "build" and "clean build" would be a good idea later, which case it will most likely derive from this. { File.Delete(targetCPKPath); } else { DirectoryGuard.CheckDirectory(targetCPKPath); } File.Copy(originalCPKPath, targetCPKPath); } if (changedFiles.Count > 0) { var batchReplaceArg = new Dictionary <string, string>(); foreach (var file in changedFiles) { string fileName = file.fileName; if (!fileName.Contains("/")) // done to match the format used by the batch replacer { fileName = "/" + fileName; } batchReplaceArg[fileName] = Path.Combine(ProjectFolder.GetRootDir(), file.filePath); } ReplaceCPKFiles(originalCPKPath, targetCPKPath, batchReplaceArg); } return(true); }
/// <summary> /// 使用默认的校验算法计算校验和 /// </summary> /// <param name="input">输入</param> /// <param name="encoding">编码</param> /// <param name="checksum">使用校验类类型</param> /// <returns>校验和</returns> public long Checksum(string input, Encoding encoding, Checksums checksum) { Guard.Requires <ArgumentNullException>(input != null); Guard.Requires <ArgumentNullException>(encoding != null); return(Checksum(encoding.GetBytes(input), checksum)); }
/// <summary> /// 使用默认的校验算法计算校验和 /// </summary> /// <param name="input">输入</param> /// <param name="checksum">使用校验类类型</param> /// <returns>校验和</returns> public long Checksum(string input, Checksums checksum) { return(Checksum(input, defaultEncoding, checksum)); }
/// <summary> /// 拓展校验算法 /// </summary> /// <param name="checksum">校验类类型</param> /// <param name="builder">构建器</param> public void Extend(Checksums checksum, Func <IChecksum> builder) { Guard.Requires <ArgumentNullException>(checksum != null); Guard.Requires <ArgumentNullException>(builder != null); checksumsMaker.Add(checksum, builder); }