Giter Site home page Giter Site logo

bcryptnet / bcrypt.net Goto Github PK

View Code? Open in Web Editor NEW
786.0 15.0 92.0 9.88 MB

BCrypt.Net - Bringing updates to the original bcrypt package

License: MIT License

C# 85.01% Batchfile 0.06% Java 14.88% Dockerfile 0.06%
blowfish bcrypt c-sharp algorithm hash netcore net microsoft nuget cipher

bcrypt.net's Introduction

SWUbanner

bcrypt.net - next

Porting of bcrypt.codeplex.com with enhanced security, missing fixes, features and better .net support.

Build status

Nuget

Download using nuget or Paket (https://fsprojects.github.io/Paket/)

Package: https://www.nuget.org/packages/BCrypt.Net-Next/ NuGet

Signed Package - https://www.nuget.org/packages/BCrypt.Net-Next.StrongName/ NuGet Signed Package

How to use

There are various examples in our test-harness folder and unit-tests

The simplest usage is as follows...

To Hash a password:

DotNet6

File-scoped namespaces are shown; imagine curly brackets if you need to.

Top level namespace

namespace DotNetSix;

using BCrypt.Net;

string passwordHash =  BCrypt.HashPassword("my password");

DotNet4/6 etc

Due to the naming of the library if the namespace is after the using statements the call changes as .net fails to resolve naming correctly I'd advise rather than enter the entire namespace you simply use the import aliasing as below.

using BC = BCrypt.Net.BCrypt;

namespace DotNetSix;

string passwordHash =  BC.HashPassword("my password");

DotNet6

You can also do aliasing at the CSProj level; and wouldn't need to add the using statement at all

This example would allow you to use an alias BC.HashPassword() in your code

    <ItemGroup>
        <!-- emits global using BcryptNet = global::BCrypt.Net.BCrypt; -->
        <Using Include="BCrypt.Net.BCrypt" Alias="BC"/>
    </ItemGroup>

This version would allow you to just call Verify and HashPassword in your code base without any other reference.

    <ItemGroup>
        <!-- emits global using static global::BCrypt.Net.BCrypt; -->
        <Using Include="BCrypt.Net.BCrypt" Static="True"/>
    </ItemGroup>

Note: Although this library allows you to supply your own salt, it is highly advisable that you allow the library to generate the salt for you. These methods are supplied to maintain compatibility and for more advanced cross-platform requirements that may necessitate their use.

To Verify a password against a hash (assuming you've stored the hash and retrieved from storage for verification):

All previous notes about namespacing apply here too

BCrypt.Verify("my password", passwordHash);

This implementation on hashing will generate a salt automatically for you with the work factor (2^number of rounds) set to 11 (which matches the default across most implementation and is currently viewed as a good level of security/risk).

To save you the maths a small table covering the iterations is provided below. The minimum allowed in this library is 4 for compatibility, the maximum is 31 (at 31 your processor will be wishing for death).

| Cost  | Iterations               |
|-------|--------------------------|
|   8   |    256 iterations        |
|   9   |    512 iterations        |
|  10   |  1,024 iterations        |
|  11   |  2,048 iterations        |
|  12   |  4,096 iterations        |
|  13   |  8,192 iterations        |
|  14   | 16,384 iterations        |
|  15   | 32,768 iterations        |
|  16   | 65,536 iterations        |
|  17   | 131,072 iterations       |
|  18   | 262,144 iterations       |
|  19   | 524,288 iterations       |
|  20   | 1,048,576 iterations     |
|  21   | 2,097,152 iterations     |
|  22   | 4,194,304 iterations     |
|  23   | 8,388,608 iterations     |
|  24   | 16,777,216 iterations    |
|  25   | 33,554,432 iterations    |
|  26   | 67,108,864 iterations    |
|  27   | 134,217,728 iterations   |
|  28   | 268,435,456 iterations   |
|  29   | 536,870,912 iterations   |
|  30   | 1,073,741,824 iterations |
|  31   | 2,147,483,648 iterations |

etc

and a simple benchmark you can run by creating a console program, adding this BCrypt Library and using this code.

    var cost = 16;
    var timeTarget = 100; // Milliseconds
    long timeTaken;
    do
    {
        var sw = Stopwatch.StartNew();

        BCrypt.HashPassword("RwiKnN>9xg3*C)1AZl.)y8f_:GCz,vt3T]PI", workFactor: cost);

        sw.Stop();
        timeTaken = sw.ElapsedMilliseconds;

        cost -= 1;

    } while ((timeTaken) >= timeTarget);

    Console.WriteLine("Appropriate Cost Found: " + (cost + 1));
    Console.ReadLine();

This will start at 16 which is 65,536 iterations and reduce the cost until the time target is reached. It's up to you what you consider an allowable time, but if it's below 10, I'd seriously advice leaving it at 10 and maybe investing in a larger server package.

Enhanced Entropy

The recommended 56 byte password limit (including null termination byte) for bcrypt relates to the 448 bit limit of the Blowfish key; Any bytes beyond that limit are not fully mixed into the hash, as such making the 72 byte absolute limit on bcrypt passwords less relevant considering what actual effect on the resulting hash by those bytes.

Other languages have handled this perceived issue by pre-hashing the passphrase/password to increase the used entropy, dropbox being one of the more public articles on this.

You can opt into enhanced hashing simply using the following code (basically prefixing the method calls with Enhanced)

var enhancedHashPassword = BCrypt.EnhancedHashPassword(myPassword);
var validatePassword = BCrypt.EnhancedVerify(myPassword, enhancedHashPassword);

By default the library uses SHA384 hashing of the passphrase, the material generated is then passed to bcrypt to form your hash via the usual bcrypt routine. If you want to specify a different version of SHA, or just wish to explicitly set in your code the version used in case it ever changes in a major release of the library, you can do so by using the following change to the above.

var enhancedHashPassword = BCrypt.EnhancedHashPassword(myPassword, hashType: HashType.SHA384);
var validatePassword = BCrypt.EnhancedVerify(myPassword, enhancedHashPassword, hashType:HashType.SHA384);

Why SHA384? It's a good balance of performance, security, collision protection and is the only version that wasn't vulnerable to length extension attacks https://blog.skullsecurity.org/2012/everything-you-need-to-know-about-hash-length-extension-attacks .

Should I use Enhanced Entropy? You lose nothing by using it

Why would I need to change the SHA type? Some libraries like PassLib hash using SHA256, so mostly a compatibility thing. DropBox used SHA512 so if you worked at dropbox you'd want compatibility. The enhancing is mostly a convenience extension in that you could already pre-hash and pass into the standard method calls.

What does it do? We take the utf8 bytes of your password as inputBytes SHA hash them, convert to base64 (for compatibility with other language implementations) then use those bytes to perform the standard bcrypt call.

Compiling

You'll need at least VS2022 with the current SDK https://www.microsoft.com/net/download;

The nuget packages can be built by running buildfornuget.cmd or

dotnet restore .\src
dotnet pack .\src\BCrypt.Net --configuration Release

Tests

You can run the tests from the main folder by typing dotnet test .\src\BCrypt.Net.UnitTests\ Running TestGenerateSaltWithMaxWorkFactor will take significant time.

Description

A .Net port of jBCrypt implemented in C#. It uses a variant of the Blowfish encryption algorithm’s keying schedule, and introduces a work factor, which allows you to determine how expensive the hash function will be, allowing the algorithm to be "future-proof".

Details

This is, for all intents and purposes, a direct port of jBCrypt written by Damien Miller. The main differences are the addition of some convenience methods and some mild re-factoring. The easiest way to verify BCrypt.Net's parity with jBCrypt is to compare the unit tests.

For an overview of why BCrypt is important, see How to Safely Store a Password. In general, it's a hashing algorithm that can be adjusted over time to require more CPU power to generate the hashes. This, in essence, provides some protection against Moore's Law. That is, as computers get faster, this algorithm can be adjusted to require more CPU power. The more CPU power that's required to hash a given password, the more time a "hacker" must invest, per password. Since the "work factor" is embedded in the resultant hash, the hashes generated by this algorithm are forward/backward-compatible.

Why BCrypt

From How to Safely Store a Password:

It uses a variant of the Blowfish encryption algorithm’s keying schedule and introduces a work factor, which allows you to determine how expensive the hash function will be. Because of this, BCrypt can keep up with Moore’s law. As computers get faster you can increase the work factor and the hash will get slower.

Blowfish-based scheme - Versioning/BCrypt Revisions

Niels Provos and David Mazières designed a crypt() scheme called bcrypt based on Blowfish, and presented it at USENIX in 1999.[14]

The printable form of these hashes starts with $2$, $2a$, $2b$, $2x$ or $2y$ depending on which variant of the algorithm is used:

$2$ – Currently obsolete
$2a$ – The current key used to identify this scheme.
       Since a major security flaw was discovered in 2011 in a third-party implementation of the algorithm,
       hashes indicated by this string are now ambiguous and might have been generated by the flawed
       implementation, or a subsequent fixed, implementation.
       The flaw may be triggered by some password strings containing non-ASCII characters, such as specially
       crafted password strings.
$2b$ – Used by some recent implementations which include a mitigation to a wraparound problem.
       Previous versions of the algorithm have a problem with long passwords. By design, long passwords
       are truncated at 72 characters, but there is an 8-bit wraparound problem with certain password
       lengths resulting in weak hashes.
$2x$ – Post-2011 bug discovery, old hashes can be renamed to be $2x$ to indicate that they were generated with
       the broken algorithm. These hashes are still weak, but at least it's clear which algorithm was used to
       generate them.
$2y$ – Post Post-2011 bug discovery, $2y$ may be used to unambiguously use the new, corrected algorithm. On an
       implementation suffering from the bug, $2y$ simply won't work. On a newer, fixed implementation, it will
       produce the same result as using $2a$.

First and foremost this library originated as a port of jBCrypt from mindrot, and subsequently the bcrypt revision was set to match, which in this case is $2a$. This has been changed as handling only the single revision causes issues cross-platform with implementations that moved altered their revision to handle migrations and other issues.

The original bcrypt code (released in OpenBSD 2.1) identified itself as
$2$. Shortly after release, a bug was fixed and the hash identifier
changed to $2a$. Support for "minor" versions wasn't really
planned, but it was backwards compatible.

Solar Designer wrote a second implementation of bcrypt. This
reimplementation suffered from a flaw dealing with 8 bit characters
and led to the introduction of the 'x' and 'y' flavours. OpenBSD did
not have this problem and supports neither 'x' nor 'y' hash versions.

---

Solar found a bug in their OpenBSD implementation of bcrypt when hashing
long passwords. The length is stored in an unsigned char type, which
will overflow and wrap at 256. Although we consider the existence of
affected hashes very rare, in order to differentiate hashes generated
before and after the fix, we are introducing a new minor 'b'.

OpenBSD 5.5 (coming this spring) will accept and verify 'b' hashes,
although it will still generate 'a' hashes. OpenBSD 5.6 (coming this
fall) will change to generating 'b' hashes by default.

A future release of Solar's bcrypt code should also support 'b'.

There is no difference between 2a, 2x, 2y, and 2b. They all output the same result.

Refs

Releases

release notes are here https://github.com/BcryptNet/bcrypt.net/releases

v4.0.3 - Addition of .net 6 targeting; tidy up targets.

v4.0.2 - Addition of .net 5 targeting; wrap shaxxx creation in using to release.

v4.0.0 (breaking changes) - A bug in Enhanced Hashing was discovered that causes the hashes created to be inoperable between different languages. V4 provides the fix for this as well as adding test vectors from PHP and Python to ensure the issue remains fixed in the future. V4 also removes the legacy 384 option that came before Base64 was added.

v3.5.0 - A bug in Enhanced Hashing was discovered that causes the hashes created to be inoperable between different languages. As part of the fix 3.5 release contains the ability to Verify and HashPassword were given an additional v4CompatibleEnhancedEntropy parameter. This allows the user to verify their Enhanced hash as normal; then re-hash + store using V4. This functionality is purely to allow migration and is removed in V4.

v3.3.3 -Performance (heap reduction) for netcore and removal of regex https://github.com/BcryptNet/bcrypt.net/releases/tag/3.3.0

v2.1.3 -

  • Update test SDK
  • Match versions between Strong-Signed / Normal package
  • Update copyright year in metadata
  • Typo correction

v2.1.2 -

  • NetStandard2 and Net 4.7 addition
  • Correct typo in PasswordNeedsReshash to PasswordNeedsRehash
  • Consolidate config changes

v2.1.1 -

  • Minor csproj changes / typo

v2.1.0 -

  • Adds enhanced mode; enhanced hashing allows you to opt-in to ensuring optimal entropy on your users passwords by first making use of the fast SHA384 algorithm before BCrypt hashes the password.
  • Added Hash interrogation to allow a hash to be passed in and its component parts are returned.
  • Added timeouts to regex and set compiler flags for msbuild so < .net 4.5 (where timeouts were added to regex) we use old regex method.
  • Alter safe equals from ceq/and to xor/and/ceq moving the check outside of the loop to mitigate against branch prediction causing a timing leak
  • Add new method PasswordNeedsReshash(string hash, int newMinimumWorkLoad) as a helper method for developers to use when logging a user in to increase legacy workloads
  • Add ValidateAndReplacePassword method to allow inline password validation and replacement. Throws BcryptAuthenticationException in the event of authentication failure.
  • Cleaned up XML-doc for intellisense
  • Increased compatibility by allowing BCrypt revisions from other frameworks/languages to be validated and generated whilst maintaining compatibility.
  • VS2017 RTW changes

v2.0.1 -

  • Corrects usage of Secure random number generator
  • Change UTF8 handling to safer default (throwOnInvalidBytes: true)
  • .NET Encoding.UTF8 encoding instance does not raise exceptions used to encode bytes which cannot represent a valid encoding & will return the same 'unknown' character instead. This can cause entropy loss when converting from bytes to strings.
  • Change secure equals to match .net identity implementation
  • Inline vars in encipher method

v2.0.0 -

Fresh release packaged for the majority of .net & containing safe-equals to reduce the risks from timing attacks https://en.wikipedia.org/wiki/Timing_attack / https://cryptocoding.net/index.php/Coding_rules#Compare_secret_strings_in_constant_time Technically the implementation details of BCrypt theoretically mitigate against timing attacks. But the Bcrypt.net official validation function was vulnerable to timing attacks as it returned as soon as a non-matching byte was found in the hash comparison.

bcrypt.net's People

Contributors

antja0 avatar chrismckee avatar dependabot-preview[bot] avatar dependabot-support avatar dependabot[bot] avatar jfoshee avatar kaiza avatar kmasubhani avatar kristofferk avatar liammorrow avatar mammo avatar mus65 avatar naile avatar paragonie-scott avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bcrypt.net's Issues

Problem on BCrypt.Verify()

Here is my code to reproduce:

Security.cs
Security is a static class

private static readonly string strongSalt = "";  // will be assigned a value soon

public static string EncryptString(string input)
{
  try
  {
    string saltedInput = $"{input}{strongSalt}";
    string hash = BCrypt.Net.BCrypt.HashPassword(saltedInput);

    return hash;
  }
  catch(Exception ex)
  {
    Logging.Instance.Error($"(Security/{MethodBase.GetCurrentMethod().Name}): {ex.Message}");
    throw ex;
  }
}

public static bool VerifyHash(string input, string hash)
{
  try
  {
    string saltedInput = $"{input}{strongSalt}";
    return BCrypt.Net.BCrypt.Verify(saltedInput, hash);
  }
  catch(Exception ex)
  {
    Logging.Instance.Error($"(Security/{MethodBase.GetCurrentMethod().Name}): {ex.Message}");
    throw ex;
  }
}

User-Model
Hash is calculated as the same as ConfirmHash

public class UserRegistration
{
  public ObjectId _id { get; set; }

  public string Email { get; set; }
  public string Hash { get; set; }

  public bool Confirmed { get; set; }
  public string ConfirmHash { get; set; }

  public UserRegistration(string Email, string Hash)
  {
    this.Email = Email;
    this.Hash = Hash;
    this.Confirmed = false;
    this.ConfirmHash = Security.EncryptString(this.Email);
  }
}

As you can see, on MongoDB I store the hash string...
db_data

Login-logic

bool equal = Security.VerifyHash(password, document.Hash);
return (equal == true ? document.Hash : string.Empty);

Same password of registering process... i get false everytime.
What's the problem? Thanks :)

'That assembly does not allow partially trusted callers' Error on shared hosting platforms.

I am trying to use this library in a project I am working on. Locally it is working fine, however as soon as I deploy it to my (shared) webhost, I get the error "That assembly does not allow partially trusted callers". I have no access to IIS on the server, because it's a shared hosting platform.

From what I have read this can be fixed by addding [assembly:AllowPartiallyTrustedCallers] to the assemblyinfo.cs file. I would assume more people would have run into this, so there might be an easier solution?

Verify password not working

I have tried it before in previous versions, but it didn't work out. I'm trying it again and it doesn't work out. The error is seen below.

SaltParseException: Invalid salt version
BCrypt.Net.BCrypt.HashPassword(string inputKey, string salt, bool enhancedEntropy, HashType hashType) in BCrypt.cs, line 633

In the documentation, it says to use this but it doesn't work out. BCrypt.Verify("my password", passwordHash);

As a workaround, I had to separately store the hash and then compare it instead. Hope to be able to fix the Bcrypt.Verify bug as it makes the code much cleaner.

var userSuppliedPasswordhash = BCryptPasswordHash.HashPassword(<user password>, <salt>);

if (storedPassword.Equals(userSuppliedPasswordhash)){
...
}

SHA instances aren't disposed

AFAIU, an instance of SHA256, SHA384 or SHA512 should be disposed after use since it implements IDisposable. E.g., in the documentation they wrap the SHA-related code with using statement. I guess the same should be done here:

private static byte[] EnhancedHash(byte[] inputBytes, HashType hashType)
{
    switch (hashType)
    {
        case HashType.SHA256:
            inputBytes = SafeUTF8.GetBytes(Convert.ToBase64String(SHA256.Create().ComputeHash(inputBytes)));
            break;
        case HashType.SHA384:
            inputBytes = SafeUTF8.GetBytes(Convert.ToBase64String(SHA384.Create().ComputeHash(inputBytes)));
            break;
        case HashType.SHA512:
            inputBytes = SafeUTF8.GetBytes(Convert.ToBase64String(SHA512.Create().ComputeHash(inputBytes)));
            break;
        case HashType.Legacy384:
            inputBytes = SHA384.Create().ComputeHash(inputBytes);
            break;
        default:
            throw new ArgumentOutOfRangeException(nameof(hashType), hashType, null);
    }

    return inputBytes;
}

Validate & Replace Hash

Possible impl

  • Takes hash & pass & new workload size
  • Verifies
  • Creates New Hash if valid & workload is larger then present
  • Returns replacement hash

Upgrading to version 4.0 is a breaking change

I upgraded from 3.3.3 to 4.0 and it breaks the Bcrypt.EnhancedVerify method. Meaning the old hashes created by the 3.3.3 version cannot be verified by the new version. After I downgraded to the original version, my project started to work again. For hashing I use:

Bcrypt.EnhancedHashPassword(plainPassword)

I think this is a dangerous issue.

After Upgrade from v3.2.1 to v4 password is invalid

Hello,
we did an NuGet package update to v4.0.2 for the .net 4.7.1 framework.
After that BCrypt.Net.BCrypt.EnhancedVerify returns false for an valid password.
We rolled back the package update to v3.2.1 and it worked as expected.

What's going wrong?

Best regard, Martin

Async methods?

Is there any possibility, or would it make sense to offer async versions of the methods?

Since they are by design time consuming, I thought it might be good to not block. I could wrap it in a Task.Run() but if there are awaitable versions of anything being called within the library it would be better to go all async I would think.

Thanks for making the world a better place!

Is bcrypt.net thread safe

Hi, I'm doing some investigation into hashing of passwords (it's new to me at the moment). Could you please tell me if bcrypt.net is thread safe.

Add timeouts to regexs

By default .NET regex parsing does not timeout. While the regexes look simple, and shouldn't end up infinite looping, always best to be paranoid. Shove a timeout in there :)

generateSalt does not create a 'salt'

The salt for BCrypt is an array of 16 Bytes. The generateSalt function returns a string of 28 or 29 characters, of which 6 or 7 are workfactor and version number and $-delimiters, the rest is the actual salt, base64 encoded.
why you would pass arguments like workfactor or version number to the generatehash function in the form of a string is confusing to me. giving the workfactor to the generatesalt function makes no sense. the generatesalt function doesn't use the workfactor.
So when I saw how to pass a workfactor to the hashing function, I was confused. I expected something like this:

String text ="Password"
int workfactor = 13;
string version = "2a";
//
Byte[] salt = BCrypt.GenerateSalt(); // 16B Salt, 87 fa 6f 36 a8 55 c7 df e5 0e ff 55 a3 65 05 89 e2 e0 5e 91 9d 7f
Byte[] hash = BCrypt.GenerateHash(text, salt, workfactor, version); // generates hash, e4 db 21 9f 99 5f 11 b4 09 17 bd 5e 66 0a 7c e8
String digest= BCrypt.GenerateDigest(salt, hash, workfactor, version) // "$2a$13$h/pvNqhVx9/lDv9Vo2UFieLgXpGdf9.5Nshn5lfEbQJF71eZgp86K"

I know it's a little more complicated, but certainly more accurate. Or the function names are not good.

How many rounds is the default for this library?

Hi all
On this page https://bcrypt-generator.com/ there are 12 rounds of encryption.
What is the default for this library?
In the readme, there is this sentence: This implementation on hashing will generate a salt automatically for you with the work factor (2^number of rounds) set to 10
is this the cost for hashing also or only for the creation of a salt?

[Question] Use in production?

I need to port a node app to .net core and I need a bcrypt library. There are many available, but this one seems to be the most used. That said it's nowhere near as heavily used or reviewed as the node implementation, only has one maintainer, and hasn't undergone a review.

Is anyone using this in a "serious" production system? Any tips/advice to share?

(Not trolling you @ChrisMcKee, I really appreciate the work you've done and am eager to use this... just need to tick some boxes first, you know how it is! 😄 And the framework doesn't have bcrypt built-in which is very sad!).

Custom exception types not marked as serializable

The custom exception types of the library are not marked as Serializable and are causing this exception: System.Runtime.Serialization.SerializationException: Type 'BCrypt.Net.SaltParseException' in Assembly 'BCrypt.Net-Next.StrongName, Version=3.1.0.0, Culture=neutral, PublicKeyToken=1e11be04b6288443' is not marked as serializable. When they are received by a caller in a different AppDomain in my implementation.
All 3 types have no particular customized details preventing them from being serializable, so I hope this is as simple as adding the [Serializable()] attribute before their declarations.

Fix typo in function name

Hello,

this function name caught my eye:

public static bool PasswordNeedsReshash(...)

Surely it should be Rehash, not Reshash?

Verify password failing with 3.1.3

Hi,
I am currently working on a new project based on .Net core 2.0 and trying to use the BCrypt.Net-Next package to hash and verify my passwords. I have my old project with all the users; which is a .net framework 4.6.x and uses BCrypt-official.

I am unable to verify the passwords for those users with the new project. Is it because BCrypt-official uses a different salt/work factor that BCrypt.Net-Next? If I reset the password using BCrypt-official, BCrypt.net-next can verify it and log me in. So I am a little confused as to why the verify from creating it in BCrypt.net-next does not work.

Sorry, I am very new to BCrypt and security in general and was wondering what the difference b/w BCrypt.net-next and BCrypt.net-next.StrongName.

Reduce memory usage

I was looking through the source code of the library, and noticed that there is quite some room to reduce the amount of memory allocated for certain operations.

I ran benchmarks (source code attached) for the (I think) most commonly used public methods, and came up with these results.

Obviously a large part of the runtime and memory usage is inherent to the algorithm itself, but I do believe that some optimizations might be useful to reduce unnecessary allocations.

Things I noticed at first glance:

  • StringBuilders are used as scratch buffers, preallocating might be possible.
  • The hash interrogation regexes generate a lot of garbage, a fast path for PasswordNeedsRehash could be added to improve this scenario. A simple custom parser could improve this significantly.
  • .NET Standard 2.1 has a lot of Span based API's that could be used to implement optimized paths if the consumer is compatible.

Are you interested in taking PR's for this?

BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18363
Intel Core i7-3632QM CPU 2.20GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.1.101
  [Host]     : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT
  DefaultJob : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT

Method text hash value Mean Error StdDev Gen 0 Gen 1 Gen 2 Allocated
GenerateSalt ? ? ? 748.4 ns 5.92 ns 4.95 ns 0.2155 - - 680 B
PasswordNeedsRehash ? ? ? 3,395.1 ns 37.28 ns 31.13 ns 0.4196 - - 1328 B
VerifyPassword **** $2a$0(...)eX1s. [60] ? 7,105,149.9 ns 43,366.64 ns 36,213.11 ns - - - 10064 B
HashPassword ? ? **** 7,015,698.7 ns 48,091.09 ns 44,984.44 ns - - - 10405 B
VerifyPassword abcde(...)vwxyz [26] $2a$0(...)QhstC [60] ? 7,090,475.1 ns 18,558.79 ns 15,497.43 ns - - - 10174 B
HashPassword ? ? abcde(...)vwxyz [26] 7,154,595.2 ns 85,278.99 ns 79,770.02 ns - - - 10515 B
    [MemoryDiagnoser]
    public class Benchmarks
    {
        [Benchmark]
        public string GenerateSalt()
            => BCrypt.GenerateSalt(10);

        [Benchmark]
        public bool PasswordNeedsRehash()
            => BCrypt.PasswordNeedsRehash("$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s.", 10);

        [Benchmark]
        [Arguments("")]
        [Arguments("abcdefghijklmnopqrstuvwxyz")]
        public string HashPassword(string value)
            => BCrypt.HashPassword(value, "$2a$06$DCq7YPn5Rq63x1Lad4cll.", true, HashType.SHA384);

        [Benchmark]
        [Arguments("", "$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s.")]
        [Arguments("abcdefghijklmnopqrstuvwxyz", "$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC")]
        public bool VerifyPassword(string text, string hash)
            => BCrypt.Verify(text, hash);
    }

Update: Aligned benchmarks to all use workfactor 6 hashes

Verify() :: Unknown Salt - Porting to NetCore Version

HI, I think I have a similar situation to other issues on here.

I have been using this for a while now but with with an older api that i inherited and i don't know the salt value. We are porting it to netcore and noticed the same call to verify works in the older lib but with this new one. Any ideas?

The hashed passwords all come from a financial db that we call into to so i have no idea how they are hashed.

All we did before was provide a password and a matching hash was found linked to the email address and then this was pushed into Verify().

So i am not so sure now what to do?

Enhanced Entropy documentation

First I have to say: thank you for your work on this library.

Regarding how to use Enchanced Entropy, I didn't understand the description in the readme about it.
https://github.com/BcryptNet/bcrypt.net#enhanced-entropy

Could you please clarify? Also, is it best to use the Enchanced Entropy or not; or does it depend on something?
I'm guessing it's not compatible with previous version, I mean that we'll need to make sure we either call Verify or EnhancedVerify based on what hashed password we have stored. And how to know what HashType we pass into EnchancedVerify.

Thanks.

Base64 Encode HMAC password

From #15
B64 encode hmac before cryptraw; list of libs doing same in issue
Need to use one as an example to check bcrypt b64 vs .net b64

First run from BCrypt.HashPassword() takes more time

Hello,

I have recently created a quick project to be able to find the best cost for our server. The code is really simple and available in this Gist: https://gist.github.com/Indigo744/b6cce04b8a1bfcef90a43ea1302c4701

I noticed that the first call to BCrypt.HashPassword() will always yield a longer time than expected:

Testing cost value of 4: ... took 29ms
Testing cost value of 5: ... took 4ms
Testing cost value of 6: ... took 8ms
Testing cost value of 7: ... took 16ms

If I add before the do...while() loop a simple "warmup" call like BCrypt.HashPassword("t", 4);, then I get correct results:

Testing cost value of 4: ... took 2ms
Testing cost value of 5: ... took 4ms
Testing cost value of 6: ... took 8ms
Testing cost value of 7: ... took 16ms

What do you think?

I suspect a lazy initialization from .Net somewhere but I'm not sure.
Maybe it corresponds to the time it takes to load the huge SOrig static array in memory?
I would appreciate your thoughts on this.

Thank you!

Protection against RAM attacks

Good day,
first of all thank you for a great library, awesome job, much appreciated.

I am using your library in a project where cyber security is vital. In order to comply with some external requirements our software must ensure that the passwords are not stored in RAM longer than they need to. As we don't not have any control over when the GC collects the password string (and the string itself is immutable in memory) it would be a good idea to provide a mechanism to be able to overwrite the password in memory.

Do you think there is a chance for a feature which would include overloaded methods as below:

  • HashPassword(byte[] text... and
  • Verify(byte[] password...

Above changes would allow users to alter the contents of a password variable (because as you know we cannot do it with a string without using unsafe code) once a hash is generated or verified.

Should you be interested in including such a feature I have a sample code waiting and checked using WinDbg which would provide functionality described above.

Thank you for your time and an awesome piece of code which makes our lives easier.

Incredibly slow calls to Verify after upgrading to 3.1.3

We just upgraded from 2.1.2 to 3.1.3, without any code changes (the release notes didn't define any backward-incompatible changes), and now in our testing and production-like environments, calls to Verify are taking anywhere between 30-70 seconds.

I saw the signature of Verify changed to include two extra parameters, so we tried setting these to what seemed would emulate the behaviour in 2.1.2, but it made no difference.

Code using 2.1.2 (and tried with 3.1.3):

Bcrypt.Verify(password, response.Password);

Code using 3.1.3:

Bcrypt.Verify(password, response.Password, false, HashType.Legacy384);

Weirdly, eventually all the calls do pass as expected, but take a while. Also, the calls don't hang on my local machine environment, only in our testing and production-like environments.

Any ideas what could be wrong? We've just downgraded to 2.1.2 to remove the issue.

Prehashing causes interoperability issues

This is the fragment of the source code responsible for prehashing:

private const string Nul = "\0";

...

public static string HashPassword(string inputKey, string salt, bool enhancedEntropy, HashType hashType = DefaultEnhancedHashType)
{
    ...
    byte[] inputBytes = SafeUTF8.GetBytes(inputKey + (bcryptMinorRevision >= 'a' ? Nul : EmptyString));

    if (enhancedEntropy)
    {
        inputBytes = EnhancedHash(inputBytes, hashType);
    }

    ...

    byte[] hashed = bCrypt.CryptRaw(inputBytes, saltBytes, workFactor);

    // Generate result string
    var result = new StringBuilder(60);
    result.Append("$2").Append(bcryptMinorRevision).Append('$').Append(workFactor.ToString("D2")).Append('$');
    result.Append(EncodeBase64(saltBytes, saltBytes.Length));
    result.Append(EncodeBase64(hashed, (BfCryptCiphertext.Length * 4) - 1));

    return result.ToString();
}

...

private static byte[] EnhancedHash(byte[] inputBytes, HashType hashType)
{
    switch (hashType)
    {
        case HashType.SHA256:
            inputBytes = SafeUTF8.GetBytes(Convert.ToBase64String(SHA256.Create().ComputeHash(inputBytes)));
            break;
        case HashType.SHA384:
            inputBytes = SafeUTF8.GetBytes(Convert.ToBase64String(SHA384.Create().ComputeHash(inputBytes)));
            break;
        case HashType.SHA512:
            inputBytes = SafeUTF8.GetBytes(Convert.ToBase64String(SHA512.Create().ComputeHash(inputBytes)));
            break;
        case HashType.Legacy384:
            inputBytes = SHA384.Create().ComputeHash(inputBytes);
            break;
        default:
            throw new ArgumentOutOfRangeException(nameof(hashType), hashType, null);
    }

    return inputBytes;
}

Judging from it and simply put, the raw bytes that are passed into bcrypt hash-function are derived as:

  • if prehashing isn't used
byte[] bcryptInput = Encoding.UTF8.GetBytes(password + "\0");
  • with prehashing
byte[] passwordBytes = Encoding.UTF8.GetBytes(password + "\0");
byte[] bcryptInput = Encoding.UTF8.GetBytes(Convert.ToBase64String(SHA256.Create().ComputeHash(passwordBytes)));

This raises questions:

  1. When prehashing is used, why is null-terminating char added to the password?
    It looks like there's no reason to do that, because the password is then passed through SHA-2, which will process it anyway (with or without null-terminator), so adding it seems like unnecessary work. Maybe you could clarify.
  2. When prehashing is used, why isn't null-terminator added to the end of base64-string?
    Basically base64-string acts here like a password, which normally gets appended a null-terminator before being converted from string representation into bytes for bcrypt (as shown in the pseudo-code fragment without prehashing). Hope you can clarify this.

The second issue causes serious interoperability problem. When coding for different platform where there's no BCrypt.Net-Next library, the prehashing code can't be easily reproduced: one can generate base64-string the same exact way, but when it gets passed through bcrypt, it will automatically be appended with null-character (which doesn't happen in BCrypt.Net-Next when prehashing is used)

string password = "MyPassword";
string prehashedPassword;
using (var sha256 = SHA256.Create())
    prehashedPassword = Convert.ToBase64String(sha256.ComputeHash(Encoding.UTF8.GetBytes(password + "\0")));
// Null-terminator gets appended inside this method, before converting argument into bytes:
string hash = SomeOtherBcryptLibrary.HashPassword(prehashedPassword);

The same is true for verification:

string hash = "$2b$12$...";
string password = "MyPassword";
string prehashedPassword;
using (var sha256 = SHA256.Create())
    prehashedPassword = Convert.ToBase64String(sha256.ComputeHash(Encoding.UTF8.GetBytes(password + "\0")));
// The method computes bcrypt-hash for the supplied password and compares it to the supplied hash;
// it adds null-terminating character to the password before running it through bcrypt - something BCrypt.Net-Next doesn't do when prehashing is used:
bool isPasswordValid = SomeOtherBcryptLibrary.Verify(hash, prehashedPassword);

As a result, if prehashing is used in BCrypt.Net-Next

  • hashes generated in BCrypt.Net-Next can't be verified in a 3rd-party bcrypt library;
  • hashes generated in a 3rd-party bcrypt library can't be verified in BCrypt.Net-Next (using built-in prehashing mechanism).

The workaround is to use 3rd-bcrypt library that accepts password as bytes, not string - this will allow to reproduce the hashing steps of BCrypt.Net-Next exactly. But most libraries don't support hashing raw bytes (including BCrypt.Net-Next itself).

And back to the first issue, not adding null-terminator to the password while prehashing simplifies the code. With both issues fixed the code would look like

string password = "MyPassword";
string prehashedPassword;
using (var sha256 = SHA256.Create())
    prehashedPassword = Convert.ToBase64String(sha256.ComputeHash(Encoding.UTF8.GetBytes(password)));
string hash = AnyBcryptLibrary.HashPassword(prehashedPassword)

This code still solves the problem of long passwords for bcrypt and uses SHA-2 prehashing. But it requires an implementation of bcrypt that accepts password as a string - a functionality commonly supported in other bcrypt libraries, which makes this code highly interoperable. So, I suggest using this approach for prehashing in BCrypt.Net-Next.

EnhancedHashPassword veryfi in other language exp.: nodejs

Hello,
I have a database passwords with hasad this code :
BCrypt.Net.BCrypt.EnhancedHashPassword("test"); // the result is : // $2a$11$LSbpD8Wu2xD4mr878RM9k.2Am7A4WkqApoNzp40BagIzQmpPowh3e
And I like veryfi in other program, but the nodjs bcryt or bcryptjs cant verify this hash.
("$2a$11$LSbpD8Wu2xD4mr878RM9k.2Am7A4WkqApoNzp40BagIzQmpPowh3e")

And I can't modify the database password.
What can be the solution?
Thank you for the help.

Weird path showing up in logs

Info

BCrypt.NET-Next version 3.2.1

System.ArgumentException: Invalid salt
Parameter name: salt
at BCrypt.Net.BCrypt.HashPassword(String inputKey, String salt, Boolean enhancedEntropy, HashType hashType) in C:\Users\chris\source\repos\bcrypt.net\src\BCrypt.Net\BCrypt.cs:line 614
at BCrypt.Net.BCrypt.Verify(String text, String hash, Boolean enhancedEntropy, HashType hashType) in C:\Users\chris\source\repos\bcrypt.net\src\BCrypt.Net\BCrypt.cs:line 838

I got this error log in server logs when parsing a empty password from a database.
So basically parsing an empty string with BCrypt.Net.BCrypt.Verify()

Issue

I'm confused why the path "C:\Users\chris..." is printed in our logs.
There is no user named chris in the server. This path simply does not exist.
However, it seems like there is a contributor named chris in this repo.

Are you aware of this? Care to explain?

Framework targeting incorrect

Hello,

There seems to be an issue with the framework targeting for the NuGet package.
If you look at the package on NuGet then it appears that there are no dependencies listed. This is strange, I expected to see System.Memory somewhere.

Also, the performance improvements (that use Span) do not seem to be part of the NuGet package.
The netstandard2.1 assembly in the package still has the 'old' signatures that use arrays, as seen below in the ILdasm screenshot.

image

I believe the error is in the way the HAS_SPAN compile time constant is defined.

Currently it is:

  <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0|net452|net462|net472'">
    <PackageReference Include="System.Memory" Version="4.5.3" />
  </ItemGroup>

  <PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1|netstandard2.1|netstandard2.0|net452|net462|net472'">
    <DefineConstants>$(DefineConstants);HAS_SPAN</DefineConstants>
  </PropertyGroup>

As far as I know the MSBuild '==' operator performs an exact string match.

I believe that this is the correct way to define the dependency and constant:

  <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'
                         or '$(TargetFramework)' == 'net452'
                         or '$(TargetFramework)' == 'net462'
                         or '$(TargetFramework)' == 'net472'">
    <PackageReference Include="System.Memory" Version="4.5.3" />
  </ItemGroup>

  <PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'
                         or '$(TargetFramework)' == 'netstandard2.1'
                         or '$(TargetFramework)' == 'netstandard2.0'
                         or '$(TargetFramework)' == 'net452'
                         or '$(TargetFramework)' == 'net462'
                         or '$(TargetFramework)' == 'net472'">
    <DefineConstants>$(DefineConstants);HAS_SPAN</DefineConstants>
  </PropertyGroup>

GetWorkFactor method is not visible because HashParser is internal

Hi,

In my current project I'm migrating passwords work factor from 10 to 5. And I would like to use HashParser.GetWorkFactor method to check work factor of hashed password. But I can not since HashParser is an internal class. Ofc I can use reflection but is there any specific reason to make HashParser class as internal?

Thanks,

Hash compatibility with jBCrypt

I'm having an issue where I have a web app that was originally set to use a version of Damien Miller's jBCrypt.

Hashes with $2a$10$... work fine between jBCrypt and BCrypt.NET (the latest, v3.1.0). However, hashes with $2a$12$... consistently fail.

Should this be possible? Is there something I'm missing in setting the Verify() parameters or anything so 12 iteration hashes work like 10 iteration hashes seem to?

Enhanced Entropy: sha384 and NUL bytes

Hello,

I was reading this article (which actually recommend this library) about using bcrypt for password hashing.

It suggests to pass the password through SHA384 before using bcrypt in order to circumvent the bcrypt limitation.

I was glad to find that your library implemented it through the enhancedEntropy parameter.

However, looking at the code, I couldn't find any mention of base64 encoding after the SHA384 and prior bcrypt.

base64 is suggested in the article because:

Bcrypt truncates on NUL bytes.

And

A base64-encoded hash is guaranteed to not contain NUL bytes

Thanks for your insight.

EDIT Adding some reference implementation seen elsewhere:
In PasswordLock PHP library, they effectively perform base64 after sha384.
In passlib Python library, they also perform base64 after sha256.

System.Io.FileLoadException: BCrypt.Net-Next

With the update of netcore 3.0 to version v3.0.0-rc1 BCrypt.Net-Next gives sometimes this error:

Unhandled Exception: System.IO.FileLoadException - System.IO.FileLoadException: Could not load file or assembly 'BCrypt.Net-Next, Version=3.1.3.0, Culture=neutral, Publi
cKeyToken=1e11be04b6288443'. An operation is not legal in the current state. (0x80131509)
File name: 'BCrypt.Net-Next, Version=3.1.3.0, Culture=neutral, PublicKeyToken=1e11be04b6288443'
 ---> System.InvalidOperationException: AssemblyLoadContext is unloading or was already unloaded.
   at System.Runtime.Loader.AssemblyLoadContext.VerifyIsAlive()
   at System.Runtime.Loader.AssemblyLoadContext.LoadFromAssemblyPath(String assemblyPath)
   at AltV.Net.Host.ResourceAssemblyLoadContext.Load(AssemblyName assemblyName)
   at System.Runtime.Loader.AssemblyLoadContext.ResolveUsingLoad(AssemblyName assemblyName)
   at System.Runtime.Loader.AssemblyLoadContext.Resolve(IntPtr gchManagedAssemblyLoadContext, AssemblyName assemblyName)
   at Services.ConnectService.GetAccountByCredentialsAsync(String username, String password)

Problem verifying V2.1.3 password hash with V3.0.1

How can I verify an enhanced entropy V2 password (using a hashtype of Legacy384 I presume...?) with V3? While I can pass the hashtype to HashPassword(), there doesn't seem to be a way to pass HashType.Legacy384 to any of the "Verify()" routines.

Example: Using V2.1.3, I hashed this password "$up3r $3cre7 p4$$w0rd" to "$2a$10$QCYql.XdRqPqkMb7olo9u.p/Vvjz5N1kOUsjniTIeeIJjBwe6UBp2". With V3, neither of EnhancedVerify or Verify(...,..., true) return true for that hash.

Documentation

Markdown in folder rather than wiki. Maybe dupe for git page?

How to validate hash format?

Hi.
How to found current hash is valid?
Below method validate hash and replace new password hash:
BCrypt.ValidateAndReplacePassword
But I don't want replace hash, Just validate current hash without password like this BCrypt.ValidateHash(currentHash)
How can I do that?

Namespace and Class name conflict

using System;
using bcrypt = BCrypt.Net.BCrypt;

namespace ChatServer
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(bcrypt.Verify("123456", "$2y$10$3Li1pWPdcGTRnZHeNtCR1OPseHtXVL9TdomRaIaXaeB/xS0vgoEJ."));
        }
    }
}

Sorry for my bad english.I need to renmae the Bcrypt class and use it.
If i use it like this

using System;
using BCrypt.Net;

namespace ChatServer
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Bcrypt.Verify("123456", "$2y$10$3Li1pWPdcGTRnZHeNtCR1OPseHtXVL9TdomRaIaXaeB/xS0vgoEJ."));
        }
    }
}

I will get error Bcrypt is a namespace and do not have Verify function.

There is no type or namespace name "HashPassword" in the namespace "BCrypt"

`using System;
using BCrypt.Net;

namespace b5
{
    class Program
    {
        static void Main(string[] args)
        {
            string passwordHash = BCrypt.HashPassword("my password");
            BCrypt.Verify("my password", passwordHash);
            Console.WriteLine("Hello World!");
        }
    }
}`

This is my console app.
use nuget BCrypt.Net-Next 3.2.1.
just do not work,can you help me?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.