Skip to content

Commit 43c225a

Browse files
committed
#54 Reduce allocations through sizing / replace string builder with str.fmt / rename minor to match other use
1 parent 344cff3 commit 43c225a

4 files changed

Lines changed: 79 additions & 19 deletions

File tree

src/BCrypt.Net.sln

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio 15
4-
VisualStudioVersion = 15.0.27004.2009
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.29709.97
55
MinimumVisualStudioVersion = 15.0.26228.4
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BCrypt.Net", "BCrypt.Net\BCrypt.Net.csproj", "{CD69F016-5940-4FCA-BCA1-9D1D87C6F873}"
77
EndProject
@@ -21,6 +21,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BCrypt.Net.StrongName", "BC
2121
EndProject
2222
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BCrypt.Net.Package", "BCrypt.Net.MainPackage\BCrypt.Net.Package.csproj", "{2AB9DBD9-6C09-492F-817C-54EEDFFDA120}"
2323
EndProject
24+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmark", "Benchmark\Benchmark.csproj", "{E75AA3B8-BF28-4366-B5C6-14AF342290C3}"
25+
EndProject
2426
Global
2527
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2628
Debug|Any CPU = Debug|Any CPU
@@ -79,6 +81,18 @@ Global
7981
{2AB9DBD9-6C09-492F-817C-54EEDFFDA120}.Release|x64.Build.0 = Release|Any CPU
8082
{2AB9DBD9-6C09-492F-817C-54EEDFFDA120}.Release|x86.ActiveCfg = Release|Any CPU
8183
{2AB9DBD9-6C09-492F-817C-54EEDFFDA120}.Release|x86.Build.0 = Release|Any CPU
84+
{E75AA3B8-BF28-4366-B5C6-14AF342290C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
85+
{E75AA3B8-BF28-4366-B5C6-14AF342290C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
86+
{E75AA3B8-BF28-4366-B5C6-14AF342290C3}.Debug|x64.ActiveCfg = Debug|Any CPU
87+
{E75AA3B8-BF28-4366-B5C6-14AF342290C3}.Debug|x64.Build.0 = Debug|Any CPU
88+
{E75AA3B8-BF28-4366-B5C6-14AF342290C3}.Debug|x86.ActiveCfg = Debug|Any CPU
89+
{E75AA3B8-BF28-4366-B5C6-14AF342290C3}.Debug|x86.Build.0 = Debug|Any CPU
90+
{E75AA3B8-BF28-4366-B5C6-14AF342290C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
91+
{E75AA3B8-BF28-4366-B5C6-14AF342290C3}.Release|Any CPU.Build.0 = Release|Any CPU
92+
{E75AA3B8-BF28-4366-B5C6-14AF342290C3}.Release|x64.ActiveCfg = Release|Any CPU
93+
{E75AA3B8-BF28-4366-B5C6-14AF342290C3}.Release|x64.Build.0 = Release|Any CPU
94+
{E75AA3B8-BF28-4366-B5C6-14AF342290C3}.Release|x86.ActiveCfg = Release|Any CPU
95+
{E75AA3B8-BF28-4366-B5C6-14AF342290C3}.Release|x86.Build.0 = Release|Any CPU
8296
EndGlobalSection
8397
GlobalSection(SolutionProperties) = preSolution
8498
HideSolutionNode = FALSE

src/BCrypt.Net/BCrypt.cs

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ public static string HashPassword(string inputKey, string salt, bool enhancedEnt
616616

617617
// Determine the starting offset and validate the salt
618618
int startingOffset;
619-
char minor = (char)0;
619+
char bcryptMinorRevision = (char)0;
620620
if (salt[0] != '$' || salt[1] != '2')
621621
{
622622
throw new SaltParseException("Invalid salt version");
@@ -627,8 +627,8 @@ public static string HashPassword(string inputKey, string salt, bool enhancedEnt
627627
}
628628
else
629629
{
630-
minor = salt[2];
631-
if (minor != 'a' && minor != 'b' && minor != 'x' && minor != 'y' || salt[3] != '$')
630+
bcryptMinorRevision = salt[2];
631+
if (bcryptMinorRevision != 'a' && bcryptMinorRevision != 'b' && bcryptMinorRevision != 'x' && bcryptMinorRevision != 'y' || salt[3] != '$')
632632
{
633633
throw new SaltParseException("Invalid salt revision");
634634
}
@@ -652,7 +652,7 @@ public static string HashPassword(string inputKey, string salt, bool enhancedEnt
652652

653653
string extractedSalt = salt.Substring(startingOffset + 3, 22);
654654

655-
byte[] inputBytes = SafeUTF8.GetBytes(inputKey + (minor >= 'a' ? Nul : EmptyString));
655+
byte[] inputBytes = SafeUTF8.GetBytes(inputKey + (bcryptMinorRevision >= 'a' ? Nul : EmptyString));
656656

657657
if (enhancedEntropy)
658658
{
@@ -666,12 +666,7 @@ public static string HashPassword(string inputKey, string salt, bool enhancedEnt
666666
byte[] hashed = bCrypt.CryptRaw(inputBytes, saltBytes, workFactor);
667667

668668
// Generate result string
669-
StringBuilder result = new StringBuilder();
670-
result.AppendFormat("$2{1}${0:00}$", workFactor, minor);
671-
result.Append(EncodeBase64(saltBytes, saltBytes.Length));
672-
result.Append(EncodeBase64(hashed, (BfCryptCiphertext.Length * 4) - 1));
673-
674-
return result.ToString();
669+
return $"$2{bcryptMinorRevision}${workFactor:00}${EncodeBase64(saltBytes, saltBytes.Length)}{EncodeBase64(hashed, (BfCryptCiphertext.Length * 4) - 1)}";
675670
}
676671

677672
/// <summary>
@@ -729,10 +724,7 @@ public static string GenerateSalt(int workFactor, char bcryptMinorRevision = Def
729724

730725
RngCsp.GetBytes(saltBytes);
731726

732-
StringBuilder result = new StringBuilder();
733-
result.AppendFormat("$2{1}${0:00}$", workFactor, bcryptMinorRevision);
734-
result.Append(EncodeBase64(saltBytes, saltBytes.Length));
735-
return result.ToString();
727+
return $"$2{bcryptMinorRevision}${workFactor:00}${EncodeBase64(saltBytes, saltBytes.Length)}";
736728
}
737729

738730

@@ -748,8 +740,7 @@ public static string GenerateSalt(int workFactor, char bcryptMinorRevision = Def
748740
public static bool PasswordNeedsRehash(string hash, int newMinimumWorkLoad)
749741
{
750742
var hashInfo = InterrogateHash(hash);
751-
int currentWorkLoad;
752-
if (!Int32.TryParse(hashInfo.WorkFactor, out currentWorkLoad))
743+
if (!Int32.TryParse(hashInfo.WorkFactor, out var currentWorkLoad))
753744
{
754745
throw new ArgumentException("Work Factor (logrounds) could not be parsed", nameof(hash));
755746
}
@@ -876,7 +867,7 @@ private static string EncodeBase64(byte[] byteArray, int length)
876867
}
877868

878869
int off = 0;
879-
StringBuilder rs = new StringBuilder();
870+
StringBuilder rs = new StringBuilder(length);
880871
while (off < length)
881872
{
882873
int c1 = byteArray[off++] & 0xff;

src/Benchmark/Benchmark.csproj

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>netcoreapp3.1</TargetFramework>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<PackageReference Include="BenchmarkDotNet" Version="0.12.0" />
10+
</ItemGroup>
11+
12+
<ItemGroup>
13+
<ProjectReference Include="..\BCrypt.Net\BCrypt.Net.csproj" />
14+
</ItemGroup>
15+
16+
</Project>

src/Benchmark/Program.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System;
2+
using BenchmarkDotNet.Attributes;
3+
using BenchmarkDotNet.Running;
4+
5+
namespace BCrypt.Net.Benchmarks
6+
{
7+
class Program
8+
{
9+
static void Main(string[] args)
10+
{
11+
BenchmarkRunner.Run<Benchmarks>();
12+
Console.ReadLine();
13+
}
14+
}
15+
16+
[MemoryDiagnoser]
17+
public class Benchmarks
18+
{
19+
[Benchmark]
20+
public string GenerateSalt()
21+
=> BCrypt.GenerateSalt(10);
22+
23+
[Benchmark]
24+
public bool PasswordNeedsRehash()
25+
=> BCrypt.PasswordNeedsRehash("$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s.", 10);
26+
27+
[Benchmark]
28+
[Arguments("")]
29+
[Arguments("abcdefghijklmnopqrstuvwxyz")]
30+
public string HashPassword(string value)
31+
=> BCrypt.HashPassword(value, "$2a$06$DCq7YPn5Rq63x1Lad4cll.", true, HashType.SHA384);
32+
33+
[Benchmark]
34+
[Arguments("", "$2a$12$k42ZFHFWqBp3vWli.nIn8uYyIkbvYRvodzbfbK18SSsY.CsIQPlxO")]
35+
[Arguments("abcdefghijklmnopqrstuvwxyz", "$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq")]
36+
public bool VerifyPassword(string text, string hash)
37+
=> BCrypt.Verify(text, hash);
38+
}
39+
}

0 commit comments

Comments
 (0)