-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHashService.vb
More file actions
888 lines (791 loc) · 39.5 KB
/
Copy pathHashService.vb
File metadata and controls
888 lines (791 loc) · 39.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
Imports System.IO
Imports System.Numerics
Imports System.Security.Cryptography
Imports Org.BouncyCastle.Crypto.Digests
Public Class HashOption
Public Property Id As String = ""
Public Property DisplayName As String = ""
Public Property CatalogPropertyName As String = ""
Public Property IsDefaultEnabled As Boolean
Public Property Note As String = ""
Public Property Category As String = "Compatibility"
Public Property Detail As String = ""
End Class
Public Class FileHashes
Private _hashes As New Dictionary(Of String, String)(StringComparer.OrdinalIgnoreCase)
Public Property Blake3 As String = ""
Public Property Sha256 As String = ""
Public Property KangarooTwelve As String = ""
Public Property Sha3_256 As String = ""
Public Property Md5 As String = ""
Public Property Whirlpool As String = ""
Public Property Skein As String = ""
Public Property Hashes As Dictionary(Of String, String)
Get
Return _hashes
End Get
Set(value As Dictionary(Of String, String))
If value Is Nothing Then
_hashes = New Dictionary(Of String, String)(StringComparer.OrdinalIgnoreCase)
Else
_hashes = New Dictionary(Of String, String)(value, StringComparer.OrdinalIgnoreCase)
End If
End Set
End Property
End Class
Public Class HashRegistry
Public Shared ReadOnly Property Options As IReadOnlyList(Of HashOption) = New List(Of HashOption) From {
New HashOption With {.Id = "SHA256", .DisplayName = "SHA-256", .CatalogPropertyName = NameOf(FileHashes.Sha256), .IsDefaultEnabled = True, .Category = "Recommended", .Detail = "Default industry-standard integrity hash."},
New HashOption With {.Id = "BLAKE3", .DisplayName = "BLAKE3", .CatalogPropertyName = NameOf(FileHashes.Blake3), .IsDefaultEnabled = False, .Category = "Modern strong hashes", .Detail = "Fast modern verification for large local vaults."},
New HashOption With {.Id = "KangarooTwelve", .DisplayName = "KangarooTwelve", .CatalogPropertyName = NameOf(FileHashes.KangarooTwelve), .IsDefaultEnabled = False, .Category = "Modern strong hashes", .Detail = "Keccak-family XOF used by ARHS release workflows."},
New HashOption With {.Id = "SHA3-256", .DisplayName = "SHA3-256", .CatalogPropertyName = NameOf(FileHashes.Sha3_256), .IsDefaultEnabled = False, .Category = "Modern strong hashes", .Detail = "NIST SHA-3 family hash for alternate cryptographic evidence."},
New HashOption With {.Id = "MD5", .DisplayName = "MD5", .CatalogPropertyName = NameOf(FileHashes.Md5), .IsDefaultEnabled = False, .Category = "Legacy cryptographic hashes", .Detail = "Legacy installer and archive verification only.", .Note = "Susceptible to collision attacks; do not use for new trust decisions."},
New HashOption With {.Id = "Whirlpool", .DisplayName = "Whirlpool", .CatalogPropertyName = NameOf(FileHashes.Whirlpool), .IsDefaultEnabled = False, .Category = "Legacy cryptographic hashes", .Detail = "Older strong-hash compatibility for existing published digests."},
New HashOption With {.Id = "Skein", .DisplayName = "Skein-512", .CatalogPropertyName = NameOf(FileHashes.Skein), .IsDefaultEnabled = False, .Category = "Modern strong hashes", .Detail = "SHA-3 finalist useful when matching existing Skein digests."},
New HashOption With {.Id = "cksum-posix", .DisplayName = "cksum (POSIX)", .IsDefaultEnabled = False, .Category = "Compatibility checksums", .Detail = "Match POSIX cksum output; not cryptographic.", .Note = "Compatibility checksum; not cryptographic."},
New HashOption With {.Id = "crc8", .DisplayName = "CRC-8/SMBus", .IsDefaultEnabled = False, .Category = "Compatibility checksums", .Detail = "Small-device checksum matching; not cryptographic.", .Note = "Compatibility checksum; not cryptographic."},
New HashOption With {.Id = "crc16", .DisplayName = "CRC-16/ARC", .IsDefaultEnabled = False, .Category = "Compatibility checksums", .Detail = "Archive/device checksum matching; not cryptographic.", .Note = "Compatibility checksum; not cryptographic."},
New HashOption With {.Id = "crc32", .DisplayName = "CRC-32/IEEE", .IsDefaultEnabled = False, .Category = "Compatibility checksums", .Detail = "ZIP and transport checksum matching; not cryptographic.", .Note = "Compatibility checksum; not cryptographic."},
New HashOption With {.Id = "crc64", .DisplayName = "CRC-64/ECMA", .IsDefaultEnabled = False, .Category = "Compatibility checksums", .Detail = "Large-file checksum matching; not cryptographic.", .Note = "Compatibility checksum; not cryptographic."},
New HashOption With {.Id = "adler32", .DisplayName = "Adler-32", .IsDefaultEnabled = False, .Category = "Compatibility checksums", .Detail = "zlib-style checksum matching; not cryptographic.", .Note = "Compatibility checksum; not cryptographic."},
New HashOption With {.Id = "bsd-sum16", .DisplayName = "BSD sum16", .IsDefaultEnabled = False, .Category = "Compatibility checksums", .Detail = "Legacy Unix sum matching; not cryptographic.", .Note = "Compatibility checksum; not cryptographic."},
New HashOption With {.Id = "sysv-sum16", .DisplayName = "SYSV sum16", .IsDefaultEnabled = False, .Category = "Compatibility checksums", .Detail = "Legacy System V sum matching; not cryptographic.", .Note = "Compatibility checksum; not cryptographic."},
New HashOption With {.Id = "internet-checksum16", .DisplayName = "Internet checksum", .IsDefaultEnabled = False, .Category = "Compatibility checksums", .Detail = "RFC 1071-style checksum matching; not cryptographic.", .Note = "Compatibility checksum; not cryptographic."},
New HashOption With {.Id = "sum8", .DisplayName = "sum8", .IsDefaultEnabled = False, .Category = "Compatibility checksums", .Detail = "Simple byte-sum matching; not cryptographic.", .Note = "Compatibility checksum; not cryptographic."},
New HashOption With {.Id = "sum24", .DisplayName = "sum24", .IsDefaultEnabled = False, .Category = "Compatibility checksums", .Detail = "Simple 24-bit sum matching; not cryptographic.", .Note = "Compatibility checksum; not cryptographic."},
New HashOption With {.Id = "sum32", .DisplayName = "sum32", .IsDefaultEnabled = False, .Category = "Compatibility checksums", .Detail = "Simple 32-bit sum matching; not cryptographic.", .Note = "Compatibility checksum; not cryptographic."},
New HashOption With {.Id = "fletcher8", .DisplayName = "Fletcher-8", .IsDefaultEnabled = False, .Category = "Compatibility checksums", .Detail = "Fletcher checksum matching; not cryptographic.", .Note = "Compatibility checksum; not cryptographic."},
New HashOption With {.Id = "fletcher16", .DisplayName = "Fletcher-16", .IsDefaultEnabled = False, .Category = "Compatibility checksums", .Detail = "Fletcher checksum matching; not cryptographic.", .Note = "Compatibility checksum; not cryptographic."},
New HashOption With {.Id = "fletcher32", .DisplayName = "Fletcher-32", .IsDefaultEnabled = False, .Category = "Compatibility checksums", .Detail = "Fletcher checksum matching; not cryptographic.", .Note = "Compatibility checksum; not cryptographic."},
New HashOption With {.Id = "xor8", .DisplayName = "xor8", .IsDefaultEnabled = False, .Category = "Compatibility checksums", .Detail = "XOR checksum matching; not cryptographic.", .Note = "Compatibility checksum; not cryptographic."},
New HashOption With {.Id = "fnv1-32", .DisplayName = "FNV-1 32", .IsDefaultEnabled = False, .Category = "Compatibility non-crypto hashes", .Detail = "Legacy FNV lookup/hash matching; not cryptographic.", .Note = "Compatibility hash; not cryptographic."},
New HashOption With {.Id = "fnv1a-32", .DisplayName = "FNV-1a 32", .IsDefaultEnabled = False, .Category = "Compatibility non-crypto hashes", .Detail = "Legacy FNV-1a matching; not cryptographic.", .Note = "Compatibility hash; not cryptographic."},
New HashOption With {.Id = "fnv1a-64", .DisplayName = "FNV-1a 64", .IsDefaultEnabled = False, .Category = "Compatibility non-crypto hashes", .Detail = "Legacy 64-bit FNV-1a matching; not cryptographic.", .Note = "Compatibility hash; not cryptographic."},
New HashOption With {.Id = "jenkins-one-at-a-time32", .DisplayName = "Jenkins one-at-a-time", .IsDefaultEnabled = False, .Category = "Compatibility non-crypto hashes", .Detail = "Legacy lookup/hash matching; not cryptographic.", .Note = "Compatibility hash; not cryptographic."},
New HashOption With {.Id = "djb2-32", .DisplayName = "djb2 32", .IsDefaultEnabled = False, .Category = "Compatibility non-crypto hashes", .Detail = "Legacy string-hash matching; not cryptographic.", .Note = "Compatibility hash; not cryptographic."},
New HashOption With {.Id = "sdbm-32", .DisplayName = "SDBM 32", .IsDefaultEnabled = False, .Category = "Compatibility non-crypto hashes", .Detail = "Legacy string-hash matching; not cryptographic.", .Note = "Compatibility hash; not cryptographic."},
New HashOption With {.Id = "murmur3-32", .DisplayName = "Murmur3 32", .IsDefaultEnabled = False, .Category = "Compatibility non-crypto hashes", .Detail = "Legacy non-crypto hash matching.", .Note = "Compatibility hash; not cryptographic."},
New HashOption With {.Id = "xxhash64", .DisplayName = "xxHash64", .IsDefaultEnabled = False, .Category = "Compatibility non-crypto hashes", .Detail = "Fast non-crypto checksum matching.", .Note = "Compatibility hash; not cryptographic."}
}
Public Shared ReadOnly Property DefaultActiveHashes As String
Get
Return String.Join(",", Options.Where(Function(optionItem) optionItem.IsDefaultEnabled).Select(Function(optionItem) optionItem.Id))
End Get
End Property
Public Shared Function NormalizeActiveHashes(activeHashes As String) As String
Dim active = ParseActiveHashIds(activeHashes).ToList()
If active.Count = 0 Then
active = ParseActiveHashIds(DefaultActiveHashes).ToList()
End If
Return String.Join(",", active)
End Function
Public Shared Function ParseActiveHashIds(activeHashes As String) As IReadOnlyList(Of String)
Dim requested = If(activeHashes, "").
Split(","c).
Select(Function(value) value.Trim()).
Where(Function(value) Not String.IsNullOrWhiteSpace(value)).
ToList()
Dim normalized As New List(Of String)
For Each optionItem In Options
If requested.Any(Function(value) String.Equals(value, optionItem.Id, StringComparison.OrdinalIgnoreCase) OrElse String.Equals(value, optionItem.DisplayName, StringComparison.OrdinalIgnoreCase)) Then
normalized.Add(optionItem.Id)
End If
Next
Return normalized
End Function
Public Shared Function IsActive(activeHashes As String, hashId As String) As Boolean
Return ParseActiveHashIds(activeHashes).Contains(ResolveId(hashId), StringComparer.OrdinalIgnoreCase)
End Function
Public Shared Function GetHashValue(hashes As FileHashes, hashId As String) As String
If hashes Is Nothing Then Return ""
Select Case ResolveId(hashId)
Case "SHA256"
Return hashes.Sha256
Case "BLAKE3"
Return hashes.Blake3
Case "KangarooTwelve"
Return hashes.KangarooTwelve
Case "SHA3-256"
Return hashes.Sha3_256
Case "MD5"
Return hashes.Md5
Case "Whirlpool"
Return hashes.Whirlpool
Case "Skein"
Return hashes.Skein
Case Else
Return GetMappedHashValue(hashes.Hashes, hashId)
End Select
End Function
Public Shared Function GetArtifactHashValue(artifact As ArtifactModel, hashId As String) As String
If artifact Is Nothing Then Return ""
Select Case ResolveId(hashId)
Case "SHA256"
Return artifact.Sha256
Case "BLAKE3"
Return artifact.Blake3
Case "KangarooTwelve"
Return artifact.KangarooTwelve
Case "SHA3-256"
Return artifact.Sha3_256
Case "MD5"
Return artifact.Md5
Case "Whirlpool"
Return artifact.Whirlpool
Case "Skein"
Return artifact.Skein
Case Else
Return GetMappedHashValue(artifact.Hashes, hashId)
End Select
End Function
Public Shared Sub SetHashValue(hashes As FileHashes, hashId As String, value As String)
If hashes Is Nothing Then Return
Select Case ResolveId(hashId)
Case "SHA256"
hashes.Sha256 = If(value, "")
Case "BLAKE3"
hashes.Blake3 = If(value, "")
Case "KangarooTwelve"
hashes.KangarooTwelve = If(value, "")
Case "SHA3-256"
hashes.Sha3_256 = If(value, "")
Case "MD5"
hashes.Md5 = If(value, "")
Case "Whirlpool"
hashes.Whirlpool = If(value, "")
Case "Skein"
hashes.Skein = If(value, "")
Case Else
SetMappedHashValue(hashes.Hashes, hashId, value)
End Select
End Sub
Public Shared Sub SetArtifactHashValue(artifact As ArtifactModel, hashId As String, value As String)
If artifact Is Nothing Then Return
Select Case ResolveId(hashId)
Case "SHA256"
artifact.Sha256 = If(value, "")
Case "BLAKE3"
artifact.Blake3 = If(value, "")
Case "KangarooTwelve"
artifact.KangarooTwelve = If(value, "")
Case "SHA3-256"
artifact.Sha3_256 = If(value, "")
Case "MD5"
artifact.Md5 = If(value, "")
Case "Whirlpool"
artifact.Whirlpool = If(value, "")
Case "Skein"
artifact.Skein = If(value, "")
Case Else
SetMappedHashValue(artifact.Hashes, hashId, value)
End Select
End Sub
Public Shared Sub ApplyHashesToArtifact(artifact As ArtifactModel, hashes As FileHashes)
If artifact Is Nothing OrElse hashes Is Nothing Then Return
For Each optionItem In Options
Dim value = GetHashValue(hashes, optionItem.Id)
If Not String.IsNullOrWhiteSpace(value) Then
SetArtifactHashValue(artifact, optionItem.Id, value)
End If
Next
artifact.HashStatus = "Verified"
End Sub
Private Shared Function GetMappedHashValue(hashes As Dictionary(Of String, String), hashId As String) As String
Dim resolved = ResolveId(hashId)
If hashes Is Nothing OrElse String.IsNullOrWhiteSpace(resolved) Then Return ""
Dim value = ""
Return If(hashes.TryGetValue(resolved, value), value, "")
End Function
Private Shared Sub SetMappedHashValue(hashes As Dictionary(Of String, String), hashId As String, value As String)
Dim resolved = ResolveId(hashId)
If hashes Is Nothing OrElse String.IsNullOrWhiteSpace(resolved) Then Return
If String.IsNullOrWhiteSpace(value) Then
hashes.Remove(resolved)
Else
hashes(resolved) = value
End If
End Sub
Private Shared Function ResolveId(hashId As String) As String
Dim optionItem = Options.FirstOrDefault(Function(item) String.Equals(item.Id, hashId, StringComparison.OrdinalIgnoreCase) OrElse String.Equals(item.DisplayName, hashId, StringComparison.OrdinalIgnoreCase))
Return If(optionItem?.Id, "")
End Function
End Class
Public Class HashService
Private Const BufferSize As Integer = 1024 * 1024
Private Const Mask32 As ULong = &HFFFFFFFFUL
Private Shared ReadOnly Mask64Big As BigInteger = (BigInteger.One << 64) - BigInteger.One
Private Const Crc64EcmaPolynomial As ULong = &H42F0E1EBA9EA3693UL
Private Shared ReadOnly Crc32Table As UInteger() = BuildReflectedCrc32Table()
Private Shared ReadOnly Crc64EcmaTable As ULong() = BuildCrc64EcmaTable()
Public Overridable Function ComputeHashes(path As String, Optional activeHashes As String = "") As FileHashes
Dim hashes As New FileHashes()
Dim activeList = HashRegistry.ParseActiveHashIds(HashRegistry.NormalizeActiveHashes(activeHashes))
Dim remaining = New List(Of String)(activeList)
ComputeSharedStreamingHashes(path, remaining, hashes)
For Each hashId In remaining
HashRegistry.SetHashValue(hashes, hashId, ComputeHash(path, hashId))
Next
Return hashes
End Function
Private Shared Sub ComputeSharedStreamingHashes(path As String, activeList As List(Of String), hashes As FileHashes)
If activeList Is Nothing OrElse activeList.Count = 0 Then
Return
End If
Dim useSha256 = activeList.Remove("SHA256")
Dim useBlake3 = activeList.Remove("BLAKE3")
Dim useKangarooTwelve = activeList.Remove("KangarooTwelve")
Dim useSha3 = activeList.Remove("SHA3-256")
Dim useMd5 = activeList.Remove("MD5")
Dim useWhirlpool = activeList.Remove("Whirlpool")
Dim useSkein = activeList.Remove("Skein")
If Not (useSha256 OrElse useBlake3 OrElse useKangarooTwelve OrElse useSha3 OrElse useMd5 OrElse useWhirlpool OrElse useSkein) Then
Return
End If
Using sha256 = If(useSha256, IncrementalHash.CreateHash(HashAlgorithmName.SHA256), Nothing),
md5 = If(useMd5, IncrementalHash.CreateHash(HashAlgorithmName.MD5), Nothing),
kangaroo = If(useKangarooTwelve, New Global.StreamHash.Core.KangarooTwelve(32, Array.Empty(Of Byte)()), Nothing)
Dim blakeHasher = Blake3.Hasher.[New]()
Try
Dim sha3 = If(useSha3, New Sha3Digest(256), Nothing)
Dim whirlpool = If(useWhirlpool, New WhirlpoolDigest(), Nothing)
Dim skein = If(useSkein, New SkeinDigest(512, 512), Nothing)
ReadChunks(path,
Sub(buffer, count)
Dim chunk = buffer.AsSpan(0, count)
If sha256 IsNot Nothing Then sha256.AppendData(chunk)
If useBlake3 Then blakeHasher.Update(chunk)
If kangaroo IsNot Nothing Then kangaroo.Update(buffer, 0, count)
If sha3 IsNot Nothing Then sha3.BlockUpdate(buffer, 0, count)
If md5 IsNot Nothing Then md5.AppendData(chunk)
If whirlpool IsNot Nothing Then whirlpool.BlockUpdate(buffer, 0, count)
If skein IsNot Nothing Then skein.BlockUpdate(buffer, 0, count)
End Sub)
If sha256 IsNot Nothing Then hashes.Sha256 = Convert.ToHexString(sha256.GetHashAndReset()).ToLowerInvariant()
If useBlake3 Then
Dim blakeOutput(Blake3.Hash.Size - 1) As Byte
blakeHasher.Finalize(blakeOutput)
hashes.Blake3 = Convert.ToHexString(blakeOutput).ToLowerInvariant()
End If
If kangaroo IsNot Nothing Then hashes.KangarooTwelve = kangaroo.FinalizeHex()
If sha3 IsNot Nothing Then hashes.Sha3_256 = FinalizeBouncyDigest(sha3)
If md5 IsNot Nothing Then hashes.Md5 = Convert.ToHexString(md5.GetHashAndReset()).ToLowerInvariant()
If whirlpool IsNot Nothing Then hashes.Whirlpool = FinalizeBouncyDigest(whirlpool)
If skein IsNot Nothing Then hashes.Skein = FinalizeBouncyDigest(skein)
Finally
blakeHasher.Dispose()
End Try
End Using
End Sub
Public Function ComputeBlake3(path As String) As String
Using stream = File.OpenRead(path)
Using blakeStream As New Blake3.Blake3Stream(stream, dispose:=False)
blakeStream.CopyTo(System.IO.Stream.Null)
Return blakeStream.ComputeHash().ToString()
End Using
End Using
End Function
Public Function ComputeSha256(path As String) As String
Using stream = File.OpenRead(path)
Dim hash = SHA256.HashData(stream)
Return Convert.ToHexString(hash).ToLowerInvariant()
End Using
End Function
Public Function ComputeKangarooTwelve(path As String) As String
Using hasher As New Global.StreamHash.Core.KangarooTwelve(32, Array.Empty(Of Byte)())
ReadChunks(path, Sub(buffer, count) hasher.Update(buffer, 0, count))
Return hasher.FinalizeHex()
End Using
End Function
Public Function ComputeSha3_256(path As String) As String
Return ComputeBouncyDigest(path, New Sha3Digest(256))
End Function
Public Function ComputeMd5(path As String) As String
Using stream = File.OpenRead(path)
Dim hash = MD5.HashData(stream)
Return Convert.ToHexString(hash).ToLowerInvariant()
End Using
End Function
Public Function ComputeWhirlpool(path As String) As String
Return ComputeBouncyDigest(path, New WhirlpoolDigest())
End Function
Public Function ComputeSkein(path As String) As String
Return ComputeBouncyDigest(path, New SkeinDigest(512, 512))
End Function
Private Function ComputeHash(path As String, hashId As String) As String
Select Case hashId
Case "SHA256"
Return ComputeSha256(path)
Case "BLAKE3"
Return ComputeBlake3(path)
Case "KangarooTwelve"
Return ComputeKangarooTwelve(path)
Case "SHA3-256"
Return ComputeSha3_256(path)
Case "MD5"
Return ComputeMd5(path)
Case "Whirlpool"
Return ComputeWhirlpool(path)
Case "Skein"
Return ComputeSkein(path)
Case "cksum-posix"
Return ComputePosixCksum(path)
Case "crc8"
Return ComputeCrc8(path)
Case "crc16"
Return ComputeCrc16Arc(path)
Case "crc32"
Return ComputeCrc32(path)
Case "crc64"
Return ComputeCrc64Ecma(path)
Case "adler32"
Return ComputeAdler32(path)
Case "bsd-sum16"
Return ComputeBsdSum16(path)
Case "sysv-sum16"
Return ComputeSysvSum16(path)
Case "internet-checksum16"
Return ComputeInternetChecksum16(path)
Case "sum8"
Return ComputeSimpleSum(path, 8)
Case "sum24"
Return ComputeSimpleSum(path, 24)
Case "sum32"
Return ComputeSimpleSum(path, 32)
Case "fletcher8"
Return ComputeFletcher8(path)
Case "fletcher16"
Return ComputeFletcher16(path)
Case "fletcher32"
Return ComputeFletcher32(path)
Case "xor8"
Return ComputeXor8(path)
Case "fnv1-32"
Return ComputeFnv132(path)
Case "fnv1a-32"
Return ComputeFnv1a32(path)
Case "fnv1a-64"
Return ComputeFnv1a64(path)
Case "jenkins-one-at-a-time32"
Return ComputeJenkinsOneAtATime32(path)
Case "djb2-32"
Return ComputeDjb232(path)
Case "sdbm-32"
Return ComputeSdbm32(path)
Case "murmur3-32"
Return ComputeMurmur3_32(path)
Case "xxhash64"
Return ComputeXxHash64(path)
Case Else
Return ""
End Select
End Function
Private Shared Function ComputeBouncyDigest(path As String, digest As Org.BouncyCastle.Crypto.IDigest) As String
ReadChunks(path, Sub(buffer, count) digest.BlockUpdate(buffer, 0, count))
Return FinalizeBouncyDigest(digest)
End Function
Private Shared Function FinalizeBouncyDigest(digest As Org.BouncyCastle.Crypto.IDigest) As String
Dim output As Byte() = New Byte(digest.GetDigestSize() - 1) {}
digest.DoFinal(output, 0)
Return Convert.ToHexString(output).ToLowerInvariant()
End Function
Private Shared Function ComputePosixCksum(path As String) As String
Dim crc As UInteger = 0UI
Dim totalLength As ULong = 0UL
ReadChunks(path,
Sub(buffer, count)
For index = 0 To count - 1
crc = PosixCrcUpdate(crc, buffer(index))
Next
totalLength += CULng(count)
End Sub)
Dim lengthValue = totalLength
While lengthValue > 0UL
crc = PosixCrcUpdate(crc, CByte(lengthValue And &HFFUL))
lengthValue >>= 8
End While
Return (Not crc).ToString("x8")
End Function
Private Shared Function ComputeCrc8(path As String) As String
Dim crc As Integer = 0
ReadChunks(path,
Sub(buffer, count)
For index = 0 To count - 1
crc = crc Xor buffer(index)
For bit = 0 To 7
If (crc And &H80) <> 0 Then
crc = ((crc << 1) Xor &H07) And &HFF
Else
crc = (crc << 1) And &HFF
End If
Next
Next
End Sub)
Return crc.ToString("x2")
End Function
Private Shared Function ComputeCrc16Arc(path As String) As String
Dim crc As UInteger = 0UI
ReadChunks(path,
Sub(buffer, count)
For index = 0 To count - 1
crc = crc Xor buffer(index)
For bit = 0 To 7
If (crc And 1UI) <> 0UI Then
crc = (crc >> 1) Xor &HA001UI
Else
crc >>= 1
End If
Next
Next
End Sub)
Return (crc And &HFFFFUI).ToString("x4")
End Function
Private Shared Function ComputeCrc32(path As String) As String
Dim crc As UInteger = &HFFFFFFFFUI
ReadChunks(path,
Sub(buffer, count)
For index = 0 To count - 1
crc = (crc >> 8) Xor Crc32Table(CInt((crc Xor buffer(index)) And &HFFUI))
Next
End Sub)
Return (Not crc).ToString("x8")
End Function
Private Shared Function ComputeCrc64Ecma(path As String) As String
Dim crc As ULong = 0UL
ReadChunks(path,
Sub(buffer, count)
For index = 0 To count - 1
crc = (crc << 8) Xor Crc64EcmaTable(CInt(((crc >> 56) Xor buffer(index)) And &HFFUL))
Next
End Sub)
Return crc.ToString("x16")
End Function
Private Shared Function ComputeAdler32(path As String) As String
Const ModAdler As UInteger = 65521UI
Dim a As UInteger = 1UI
Dim b As UInteger = 0UI
ReadChunks(path,
Sub(buffer, count)
For index = 0 To count - 1
a = (a + buffer(index)) Mod ModAdler
b = (b + a) Mod ModAdler
Next
End Sub)
Return ((b << 16) Or a).ToString("x8")
End Function
Private Shared Function ComputeBsdSum16(path As String) As String
Dim sum As UInteger = 0UI
ReadChunks(path,
Sub(buffer, count)
For index = 0 To count - 1
sum = ((sum >> 1) Or ((sum And 1UI) << 15)) And &HFFFFUI
sum = (sum + buffer(index)) And &HFFFFUI
Next
End Sub)
Return sum.ToString("x4")
End Function
Private Shared Function ComputeSysvSum16(path As String) As String
Dim sum As UInteger = 0UI
ReadChunks(path,
Sub(buffer, count)
For index = 0 To count - 1
sum += buffer(index)
Next
End Sub)
sum = (sum And &HFFFFUI) + (sum >> 16)
sum = (sum And &HFFFFUI) + (sum >> 16)
Return (sum And &HFFFFUI).ToString("x4")
End Function
Private Shared Function ComputeInternetChecksum16(path As String) As String
Dim sum As UInteger = 0UI
Dim hasHighByte = False
Dim highByte As Byte = 0
ReadChunks(path,
Sub(buffer, count)
For index = 0 To count - 1
If Not hasHighByte Then
highByte = buffer(index)
hasHighByte = True
Else
sum += (CUInt(highByte) << 8) Or buffer(index)
sum = (sum And &HFFFFUI) + (sum >> 16)
hasHighByte = False
End If
Next
End Sub)
If hasHighByte Then
sum += CUInt(highByte) << 8
End If
While (sum >> 16) <> 0UI
sum = (sum And &HFFFFUI) + (sum >> 16)
End While
Return ((Not sum) And &HFFFFUI).ToString("x4")
End Function
Private Shared Function ComputeSimpleSum(path As String, bits As Integer) As String
Dim mask = If(bits = 32, Mask32, (1UL << bits) - 1UL)
Dim sum As ULong = 0UL
ReadChunks(path,
Sub(buffer, count)
For index = 0 To count - 1
sum = (sum + buffer(index)) And mask
Next
End Sub)
Return sum.ToString("x" & (bits \ 4).ToString())
End Function
Private Shared Function ComputeFletcher8(path As String) As String
Dim sum1 As Integer = 0
Dim sum2 As Integer = 0
ReadChunks(path,
Sub(buffer, count)
For index = 0 To count - 1
sum1 = (sum1 + (buffer(index) And &HF)) Mod 15
sum2 = (sum2 + sum1) Mod 15
sum1 = (sum1 + (buffer(index) >> 4)) Mod 15
sum2 = (sum2 + sum1) Mod 15
Next
End Sub)
Return ((sum2 << 4) Or sum1).ToString("x2")
End Function
Private Shared Function ComputeFletcher16(path As String) As String
Dim sum1 As Integer = 0
Dim sum2 As Integer = 0
ReadChunks(path,
Sub(buffer, count)
For index = 0 To count - 1
sum1 = (sum1 + buffer(index)) Mod 255
sum2 = (sum2 + sum1) Mod 255
Next
End Sub)
Return ((sum2 << 8) Or sum1).ToString("x4")
End Function
Private Shared Function ComputeFletcher32(path As String) As String
Dim sum1 As UInteger = 0UI
Dim sum2 As UInteger = 0UI
Dim hasHighByte = False
Dim highByte As Byte = 0
ReadChunks(path,
Sub(buffer, count)
For index = 0 To count - 1
If Not hasHighByte Then
highByte = buffer(index)
hasHighByte = True
Else
Dim word = (CUInt(highByte) << 8) Or buffer(index)
sum1 = (sum1 + word) Mod 65535UI
sum2 = (sum2 + sum1) Mod 65535UI
hasHighByte = False
End If
Next
End Sub)
If hasHighByte Then
Dim word = CUInt(highByte) << 8
sum1 = (sum1 + word) Mod 65535UI
sum2 = (sum2 + sum1) Mod 65535UI
End If
Return ((sum2 << 16) Or sum1).ToString("x8")
End Function
Private Shared Function ComputeXor8(path As String) As String
Dim value As Byte = 0
ReadChunks(path,
Sub(buffer, count)
For index = 0 To count - 1
value = value Xor buffer(index)
Next
End Sub)
Return value.ToString("x2")
End Function
Private Shared Function ComputeFnv132(path As String) As String
Dim hash As ULong = &H811C9DC5UL
ReadChunks(path,
Sub(buffer, count)
For index = 0 To count - 1
hash = (hash * &H1000193UL) And Mask32
hash = hash Xor buffer(index)
Next
End Sub)
Return hash.ToString("x8")
End Function
Private Shared Function ComputeFnv1a32(path As String) As String
Dim hash As ULong = &H811C9DC5UL
ReadChunks(path,
Sub(buffer, count)
For index = 0 To count - 1
hash = hash Xor buffer(index)
hash = (hash * &H1000193UL) And Mask32
Next
End Sub)
Return hash.ToString("x8")
End Function
Private Shared Function ComputeFnv1a64(path As String) As String
Dim hash As BigInteger = &HCBF29CE484222325UL
Dim prime As BigInteger = &H100000001B3UL
ReadChunks(path,
Sub(buffer, count)
For index = 0 To count - 1
hash = hash Xor buffer(index)
hash = (hash * prime) And Mask64Big
Next
End Sub)
Return CULng(hash).ToString("x16")
End Function
Private Shared Function ComputeJenkinsOneAtATime32(path As String) As String
Dim hash As ULong = 0UL
ReadChunks(path,
Sub(buffer, count)
For index = 0 To count - 1
hash = (hash + buffer(index)) And Mask32
hash = (hash + ((hash << 10) And Mask32)) And Mask32
hash = (hash Xor (hash >> 6)) And Mask32
Next
End Sub)
hash = (hash + ((hash << 3) And Mask32)) And Mask32
hash = (hash Xor (hash >> 11)) And Mask32
hash = (hash + ((hash << 15) And Mask32)) And Mask32
Return hash.ToString("x8")
End Function
Private Shared Function ComputeDjb232(path As String) As String
Dim hash As ULong = 5381UL
ReadChunks(path,
Sub(buffer, count)
For index = 0 To count - 1
hash = (((hash << 5) + hash) + buffer(index)) And Mask32
Next
End Sub)
Return hash.ToString("x8")
End Function
Private Shared Function ComputeSdbm32(path As String) As String
Dim hash As ULong = 0UL
ReadChunks(path,
Sub(buffer, count)
For index = 0 To count - 1
hash = (buffer(index) + ((hash << 6) And Mask32) + ((hash << 16) And Mask32) - hash) And Mask32
Next
End Sub)
Return hash.ToString("x8")
End Function
Private Shared Function ComputeMurmur3_32(path As String) As String
Const c1 As UInteger = &HCC9E2D51UI
Const c2 As UInteger = &H1B873593UI
Dim hash As UInteger = 0UI
Dim totalLength As ULong = 0UL
Dim tailBytes As New List(Of Byte)(4)
ReadChunks(path,
Sub(buffer, count)
totalLength += CULng(count)
Dim offset = 0
If tailBytes.Count > 0 Then
While tailBytes.Count < 4 AndAlso offset < count
tailBytes.Add(buffer(offset))
offset += 1
End While
If tailBytes.Count = 4 Then
MixMurmurBlock(hash, tailBytes(0), tailBytes(1), tailBytes(2), tailBytes(3), c1, c2)
tailBytes.Clear()
End If
End If
While offset + 4 <= count
MixMurmurBlock(hash, buffer(offset), buffer(offset + 1), buffer(offset + 2), buffer(offset + 3), c1, c2)
offset += 4
End While
While offset < count
tailBytes.Add(buffer(offset))
offset += 1
End While
End Sub)
Dim tail As UInteger = 0UI
Dim remaining = tailBytes.Count
If remaining = 3 Then tail = tail Xor (CUInt(tailBytes(2)) << 16)
If remaining >= 2 Then tail = tail Xor (CUInt(tailBytes(1)) << 8)
If remaining >= 1 Then
tail = tail Xor tailBytes(0)
tail = Mul32(tail, c1)
tail = RotateLeft32(tail, 15)
tail = Mul32(tail, c2)
hash = hash Xor tail
End If
hash = hash Xor CUInt(totalLength And &HFFFFFFFFUL)
hash = Fmix32(hash)
Return hash.ToString("x8")
End Function
Private Shared Sub MixMurmurBlock(ByRef hash As UInteger, b0 As Byte, b1 As Byte, b2 As Byte, b3 As Byte, c1 As UInteger, c2 As UInteger)
Dim k = CUInt(b0) Or (CUInt(b1) << 8) Or (CUInt(b2) << 16) Or (CUInt(b3) << 24)
k = Mul32(k, c1)
k = RotateLeft32(k, 15)
k = Mul32(k, c2)
hash = hash Xor k
hash = RotateLeft32(hash, 13)
hash = Add32(Mul32(hash, 5UI), &HE6546B64UI)
End Sub
Private Shared Function ComputeXxHash64(path As String) As String
Dim hash As New System.IO.Hashing.XxHash64()
ReadChunks(path, Sub(buffer, count) hash.Append(buffer.AsSpan(0, count)))
Return Convert.ToHexString(hash.GetCurrentHash()).ToLowerInvariant()
End Function
Private Shared Sub ReadChunks(path As String, onChunk As Action(Of Byte(), Integer))
Using stream = File.OpenRead(path)
Dim buffer As Byte() = New Byte(BufferSize - 1) {}
While True
Dim bytesRead = stream.Read(buffer, 0, buffer.Length)
If bytesRead <= 0 Then Exit While
onChunk(buffer, bytesRead)
End While
End Using
End Sub
Private Shared Function PosixCrcUpdate(crc As UInteger, value As Byte) As UInteger
crc = crc Xor (CUInt(value) << 24)
For bit = 0 To 7
If (crc And &H80000000UI) <> 0UI Then
crc = (crc << 1) Xor &H4C11DB7UI
Else
crc <<= 1
End If
Next
Return crc
End Function
Private Shared Function BuildReflectedCrc32Table() As UInteger()
Dim table(255) As UInteger
For i = 0 To 255
Dim value = CUInt(i)
For bit = 0 To 7
If (value And 1UI) <> 0UI Then
value = (value >> 1) Xor &HEDB88320UI
Else
value >>= 1
End If
Next
table(i) = value
Next
Return table
End Function
Private Shared Function BuildCrc64EcmaTable() As ULong()
Dim table(255) As ULong
For i = 0 To 255
Dim value = CULng(i) << 56
For bit = 0 To 7
If (value And &H8000000000000000UL) <> 0UL Then
value = (value << 1) Xor Crc64EcmaPolynomial
Else
value <<= 1
End If
Next
table(i) = value
Next
Return table
End Function
Private Shared Function RotateLeft32(value As UInteger, bits As Integer) As UInteger
Return (value << bits) Or (value >> (32 - bits))
End Function
Private Shared Function Add32(left As UInteger, right As UInteger) As UInteger
Return CUInt((CULng(left) + CULng(right)) And Mask32)
End Function
Private Shared Function Mul32(left As UInteger, right As UInteger) As UInteger
Return CUInt((CULng(left) * CULng(right)) And Mask32)
End Function
Private Shared Function Fmix32(hash As UInteger) As UInteger
hash = hash Xor (hash >> 16)
hash = Mul32(hash, &H85EBCA6BUI)
hash = hash Xor (hash >> 13)
hash = Mul32(hash, &HC2B2AE35UI)
hash = hash Xor (hash >> 16)
Return hash
End Function
End Class