diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2018-04-17 11:40:43 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2018-04-17 11:40:43 +0700 |
commit | 97f8a8a0975a8ae356f341dd15e97c4661aecfb2 (patch) | |
tree | 61928e7cd8276b48363a0ffe378bc8a23e720b49 /crypto | |
parent | Change CCM test to use IV of 12 bytes (from bc-java). (diff) | |
download | BouncyCastle.NET-ed25519-97f8a8a0975a8ae356f341dd15e97c4661aecfb2.tar.xz |
Updated OpenBsdBCrypt to support version 2y.
Diffstat (limited to 'crypto')
-rw-r--r-- | crypto/src/crypto/generators/OpenBsdBCrypt.cs | 49 | ||||
-rw-r--r-- | crypto/test/src/crypto/test/OpenBsdBCryptTest.cs | 55 |
2 files changed, 91 insertions, 13 deletions
diff --git a/crypto/src/crypto/generators/OpenBsdBCrypt.cs b/crypto/src/crypto/generators/OpenBsdBCrypt.cs index 85c34d769..49f79f95b 100644 --- a/crypto/src/crypto/generators/OpenBsdBCrypt.cs +++ b/crypto/src/crypto/generators/OpenBsdBCrypt.cs @@ -3,6 +3,7 @@ using System.IO; using System.Text; using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; namespace Org.BouncyCastle.Crypto.Generators { @@ -33,10 +34,16 @@ namespace Org.BouncyCastle.Crypto.Generators * set up the decoding table. */ private static readonly byte[] DecodingTable = new byte[128]; - private static readonly string Version = "2a"; // previous version was not UTF-8 + private static readonly string DefaultVersion = "2y"; + private static readonly ISet AllowedVersions = new HashSet(); static OpenBsdBCrypt() { + // Presently just the Bcrypt versions. + AllowedVersions.Add("2a"); + AllowedVersions.Add("2y"); + AllowedVersions.Add("2b"); + for (int i = 0; i < DecodingTable.Length; i++) { DecodingTable[i] = (byte)0xff; @@ -56,16 +63,20 @@ namespace Org.BouncyCastle.Crypto.Generators * Creates a 60 character Bcrypt String, including * version, cost factor, salt and hash, separated by '$' * + * @param version the version, 2y,2b or 2a. (2a is not backwards compatible.) * @param cost the cost factor, treated as an exponent of 2 * @param salt a 16 byte salt * @param password the password * @return a 60 character Bcrypt String */ - private static string CreateBcryptString(byte[] password, byte[] salt, int cost) + private static string CreateBcryptString(string version, byte[] password, byte[] salt, int cost) { + if (!AllowedVersions.Contains(version)) + throw new ArgumentException("Version " + version + " is not accepted by this implementation.", "version"); + StringBuilder sb = new StringBuilder(60); sb.Append('$'); - sb.Append(Version); + sb.Append(version); sb.Append('$'); sb.Append(cost < 10 ? ("0" + cost) : cost.ToString()); sb.Append('$'); @@ -80,7 +91,8 @@ namespace Org.BouncyCastle.Crypto.Generators /** * Creates a 60 character Bcrypt String, including - * version, cost factor, salt and hash, separated by '$' + * version, cost factor, salt and hash, separated by '$' using version + * '2y'. * * @param cost the cost factor, treated as an exponent of 2 * @param salt a 16 byte salt @@ -89,6 +101,23 @@ namespace Org.BouncyCastle.Crypto.Generators */ public static string Generate(char[] password, byte[] salt, int cost) { + return Generate(DefaultVersion, password, salt, cost); + } + + /** + * Creates a 60 character Bcrypt String, including + * version, cost factor, salt and hash, separated by '$' + * + * @param version the version, may be 2b, 2y or 2a. (2a is not backwards compatible.) + * @param cost the cost factor, treated as an exponent of 2 + * @param salt a 16 byte salt + * @param password the password + * @return a 60 character Bcrypt String + */ + public static string Generate(string version, char[] password, byte[] salt, int cost) + { + if (!AllowedVersions.Contains(version)) + throw new ArgumentException("Version " + version + " is not accepted by this implementation.", "version"); if (password == null) throw new ArgumentNullException("password"); if (salt == null) @@ -109,7 +138,7 @@ namespace Org.BouncyCastle.Crypto.Generators Array.Clear(psw, 0, psw.Length); - string rv = CreateBcryptString(tmp, salt, cost); + string rv = CreateBcryptString(version, tmp, salt, cost); Array.Clear(tmp, 0, tmp.Length); @@ -133,8 +162,10 @@ namespace Org.BouncyCastle.Crypto.Generators throw new DataLengthException("Bcrypt String length: " + bcryptString.Length + ", 60 required."); if (bcryptString[0] != '$' || bcryptString[3] != '$' || bcryptString[6] != '$') throw new ArgumentException("Invalid Bcrypt String format.", "bcryptString"); - if (!bcryptString.Substring(1, 2).Equals(Version)) - throw new ArgumentException("Wrong Bcrypt version, 2a expected.", "bcryptString"); + + string version = bcryptString.Substring(1, 2); + if (!AllowedVersions.Contains(version)) + throw new ArgumentException("Bcrypt version '" + version + "' is not supported by this implementation", "bcryptString"); int cost = 0; try @@ -143,7 +174,7 @@ namespace Org.BouncyCastle.Crypto.Generators } catch (Exception nfe) { - throw new ArgumentException("Invalid cost factor: " + bcryptString.Substring(4, 2), "bcryptString"); + throw new ArgumentException("Invalid cost factor: " + bcryptString.Substring(4, 2), "bcryptString", nfe); } if (cost < 4 || cost > 31) throw new ArgumentException("Invalid cost factor: " + cost + ", 4 < cost < 31 expected."); @@ -155,7 +186,7 @@ namespace Org.BouncyCastle.Crypto.Generators int start = bcryptString.LastIndexOf('$') + 1, end = bcryptString.Length - 31; byte[] salt = DecodeSaltString(bcryptString.Substring(start, end - start)); - string newBcryptString = Generate(password, salt, cost); + string newBcryptString = Generate(version, password, salt, cost); return bcryptString.Equals(newBcryptString); } diff --git a/crypto/test/src/crypto/test/OpenBsdBCryptTest.cs b/crypto/test/src/crypto/test/OpenBsdBCryptTest.cs index 8501588ee..fabe84d6b 100644 --- a/crypto/test/src/crypto/test/OpenBsdBCryptTest.cs +++ b/crypto/test/src/crypto/test/OpenBsdBCryptTest.cs @@ -74,6 +74,28 @@ namespace Org.BouncyCastle.Crypto.Tests new string[]{"8nv;PAN~-FQ]Emh@.TKG=^.t8R0EQC0T?x9|9g4xzxYmSbBO1qDx8kv-ehh0IBv>3KWhz.Z~jUF0tt8[5U@8;5:=[v6pf.IEJ", "$2a$08$eXo9KDc1BZyybBgMurpcD.GA1/ch3XhgBnIH10Xvjc2ogZaGg3t/m"}, }; + + // 2y vectors generated from htpasswd -nB -C 12, nb leading username was removed. + private static readonly string[,] twoYVec = new string[,]{ + {"a", "$2y$12$DB3BUbYa/SsEL7kCOVji0OauTkPkB5Y1OeyfxJHM7jvMrbml5sgD2"}, + {"abc", "$2y$12$p.xODEbFcXUlHGbNxWZqAe6AA5FWupqXmN9tZea2ACDhwIx4EA2a6"}, + {"hello world", "$2y$12$wfkxITYXjNLVpEi9nOjz7uXMhCXKSTY7O2y7X4bwY89aGSvRziguq"}, + {"ABCDEFGHIJKLMNOPQRSTUVWXYABCDEFGHIJKLMNOPQRSTUVWXYABCDEFGHIJKLMNOPQRSTUVWXYABCDEFGHIJKLMNOPQRSTUVWXY", "$2y$12$QwAt5kuG68nW7v.87q0QPuwdki3romFc/RU/RV3Qqk4FPw6WdbQzu"} + }; + + // Same as 2y vectors only version changed to 2b to verify handling of that version. + private static readonly string[,] twoBVec = new string[,]{ + {"a", "$2b$12$DB3BUbYa/SsEL7kCOVji0OauTkPkB5Y1OeyfxJHM7jvMrbml5sgD2"}, + {"abc", "$2b$12$p.xODEbFcXUlHGbNxWZqAe6AA5FWupqXmN9tZea2ACDhwIx4EA2a6"}, + {"hello world", "$2b$12$wfkxITYXjNLVpEi9nOjz7uXMhCXKSTY7O2y7X4bwY89aGSvRziguq"}, + {"ABCDEFGHIJKLMNOPQRSTUVWXYABCDEFGHIJKLMNOPQRSTUVWXYABCDEFGHIJKLMNOPQRSTUVWXYABCDEFGHIJKLMNOPQRSTUVWXY", "$2b$12$QwAt5kuG68nW7v.87q0QPuwdki3romFc/RU/RV3Qqk4FPw6WdbQzu"} + }; + + public static void Main(string[] args) + { + RunTest(new OpenBsdBCryptTest()); + } + public override string Name { get { return "OpenBsdBCrypt"; } @@ -129,11 +151,36 @@ namespace Org.BouncyCastle.Crypto.Tests Fail("test4 mismatch: " + "[" + i + "] " + password); } } - } - public static void Main(string[] args) - { - RunTest(new OpenBsdBCryptTest()); + { + int lower = twoYVec.GetLowerBound(0); + int upper = twoYVec.GetUpperBound(0); + for (int i = lower; i <= upper; i++) + { + password = twoYVec[i, 0]; + encoded = twoYVec[i, 1]; + + if (!OpenBsdBCrypt.CheckPassword(encoded, password.ToCharArray())) + { + Fail("twoYVec mismatch: " + "[" + i + "] " + password); + } + } + } + + { + int lower = twoBVec.GetLowerBound(0); + int upper = twoBVec.GetUpperBound(0); + for (int i = lower; i <= upper; i++) + { + password = twoBVec[i, 0]; + encoded = twoBVec[i, 1]; + + if (!OpenBsdBCrypt.CheckPassword(encoded, password.ToCharArray())) + { + Fail("twoBVec mismatch: " + "[" + i + "] " + password); + } + } + } } [Test] |