From eb27ce694b10f066e27e217b089578ea2bf2f773 Mon Sep 17 00:00:00 2001 From: Marius Thesing Date: Mon, 31 Mar 2025 21:50:52 +0200 Subject: [PATCH] remove unused bcrypt code --- .../Security/Cryptography/Bcrypt.cs | 412 +----------------- 1 file changed, 5 insertions(+), 407 deletions(-) diff --git a/src/Renci.SshNet/Security/Cryptography/Bcrypt.cs b/src/Renci.SshNet/Security/Cryptography/Bcrypt.cs index 40ab0695c..3b0eada26 100644 --- a/src/Renci.SshNet/Security/Cryptography/Bcrypt.cs +++ b/src/Renci.SshNet/Security/Cryptography/Bcrypt.cs @@ -22,53 +22,12 @@ namespace Renci.SshNet.Security.Cryptography { - /// BCrypt implementation. - /// - /// - /// BCrypt implements OpenBSD-style Blowfish password hashing using the scheme described in - /// "A Future- - /// Adaptable Password Scheme" by Niels Provos and David Mazieres. - /// - /// - /// This password hashing system tries to thwart off-line password cracking using a - /// computationally-intensive hashing algorithm, based on Bruce Schneier's Blowfish cipher. - /// The work factor of the algorithm is parameterised, so it can be increased as computers - /// get faster. - /// - /// - /// Usage is really simple. To hash a password for the first time, call the method with a random salt, like this: - /// - /// string pw_hash = BCrypt.HashPassword(plain_password); - /// - /// To check whether a plaintext password matches one that has been hashed previously, - /// use the method: - /// - /// - /// if (BCrypt.Verify(candidate_password, stored_hash)) - /// Console.WriteLine("It matches"); - /// else - /// Console.WriteLine("It does not match"); - /// - /// - /// The method takes an optional parameter (workFactor) that - /// determines the computational complexity of the hashing: - /// - /// - /// string strong_salt = BCrypt.GenerateSalt(10); - /// string stronger_salt = BCrypt.GenerateSalt(12); - /// - /// - /// The amount of work increases exponentially (2^workFactor), so each increment is twice - /// as much work. The default workFactor is 10, and the valid range is 4 to 31. - /// - /// + /// + /// BCrypt Pbkdf implementation. + /// This was originally based on https://github.com/hierynomus/sshj/blob/master/src/main/java/com/hierynomus/sshj/userauth/keyprovider/bcrypt/BCrypt.java . + /// internal sealed class BCrypt { - // BCrypt parameters - private const int GENSALT_DEFAULT_LOG2_ROUNDS = 10; - private const int BCRYPT_SALT_LEN = 16; - // Blowfish parameters private const int BLOWFISH_NUM_ROUNDS = 16; @@ -346,299 +305,10 @@ internal sealed class BCrypt 0x66697368, 0x53776174, 0x44796e61, 0x6d697465, }; - // bcrypt IV: "OrpheanBeholderScryDoubt" - private static readonly uint[] _BfCryptCiphertext = { - 0x4f727068, 0x65616e42, 0x65686f6c, - 0x64657253, 0x63727944, 0x6f756274 - }; - - // Table for Base64 encoding - private static readonly char[] _Base64Code = { - '.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', - 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', - 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', - 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', - '6', '7', '8', '9' - }; - - // Table for Base64 decoding - private static readonly int[] _Index64 = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 0, 1, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, - -1, -1, -1, -1, -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, - -1, -1, -1, -1, -1, -1, 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, -1, -1, -1, -1, -1 - }; - // Expanded Blowfish key private uint[] _P; private uint[] _S; - /// - /// Hash a string using the OpenBSD bcrypt scheme and a salt generated by . - /// - /// Just an alias for HashPassword. - /// The string to hash. - /// The hashed string. - public static string HashString(string source) - { - return HashPassword(source); - } - - /// - /// Hash a string using the OpenBSD bcrypt scheme and a salt generated by . - /// - /// Just an alias for HashPassword. - /// The string to hash. - /// The log2 of the number of rounds of hashing to apply - the work - /// factor therefore increases as 2^workFactor. - /// The hashed string. - public static string HashString(string source, int workFactor) - { - return HashPassword(source, GenerateSalt(workFactor)); - } - - /// - /// Hash a password using the OpenBSD bcrypt scheme and a salt generated by . - /// - /// The password to hash. - /// The hashed password. - public static string HashPassword(string input) - { - return HashPassword(input, GenerateSalt()); - } - - /// - /// Hash a password using the OpenBSD bcrypt scheme and a salt generated by using the given . - /// - /// The password to hash. - /// The log2 of the number of rounds of hashing to apply - the work - /// factor therefore increases as 2^workFactor. - /// The hashed password. - public static string HashPassword(string input, int workFactor) - { - return HashPassword(input, GenerateSalt(workFactor)); - } - - /// Hash a password using the OpenBSD bcrypt scheme. - /// Thrown when one or more arguments have unsupported or - /// illegal values. - /// The password to hash. - /// the salt to hash with (perhaps generated using BCrypt.gensalt). - /// The hashed password - public static string HashPassword(string input, string salt) - { - if (input == null) - throw new ArgumentNullException("input"); - - if (string.IsNullOrEmpty(salt)) - throw new ArgumentException("Invalid salt", "salt"); - - // Determinthe starting offset and validate the salt - int startingOffset; - char minor = (char)0; - if (salt[0] != '$' || salt[1] != '2') - throw new SaltParseException("Invalid salt version"); - if (salt[2] == '$') - startingOffset = 3; - else - { - minor = salt[2]; - if (minor != 'a' || salt[3] != '$') - throw new SaltParseException("Invalid salt revision"); - startingOffset = 4; - } - - // Extract number of rounds - if (salt[startingOffset + 2] > '$') - throw new SaltParseException("Missing salt rounds"); - - // Extract details from salt - int logRounds = Convert.ToInt32(salt.Substring(startingOffset, 2)); - string extractedSalt = salt.Substring(startingOffset + 3, 22); - - byte[] inputBytes = Encoding.UTF8.GetBytes((input + (minor >= 'a' ? "\0" : ""))); - byte[] saltBytes = DecodeBase64(extractedSalt, BCRYPT_SALT_LEN); - - BCrypt bCrypt = new BCrypt(); - byte[] hashed = bCrypt.CryptRaw(inputBytes, saltBytes, logRounds); - - // Generate result string - StringBuilder result = new StringBuilder(); - result.Append("$2"); - if (minor >= 'a') - result.Append(minor); - result.AppendFormat("${0:00}$", logRounds); - result.Append(EncodeBase64(saltBytes, saltBytes.Length)); - result.Append(EncodeBase64(hashed, (_BfCryptCiphertext.Length * 4) - 1)); - return result.ToString(); - } - - /// - /// Generate a salt for use with the method. - /// - /// The log2 of the number of rounds of hashing to apply - the work - /// factor therefore increases as 2**workFactor. - /// A base64 encoded salt value. - public static string GenerateSalt(int workFactor) - { - if (workFactor < 4 || workFactor > 31) - throw new ArgumentOutOfRangeException("workFactor", "The work factor must be between 4 and 31 (inclusive)"); - - byte[] rnd = new byte[BCRYPT_SALT_LEN]; - - RandomNumberGenerator rng = RandomNumberGenerator.Create(); - - rng.GetBytes(rnd); - - StringBuilder rs = new StringBuilder(); - rs.AppendFormat("$2a${0:00}$", workFactor); - rs.Append(EncodeBase64(rnd, rnd.Length)); - return rs.ToString(); - } - - /// - /// Generate a salt for use with the method - /// selecting a reasonable default for the number of hashing rounds to apply. - /// - /// A base64 encoded salt value. - public static string GenerateSalt() - { - return GenerateSalt(GENSALT_DEFAULT_LOG2_ROUNDS); - } - - /// - /// Verifies that the hash of the given matches the provided - /// - /// - /// The text to verify. - /// The previously-hashed password. - /// true if the passwords match, false otherwise. - public static bool Verify(string text, string hash) - { - return hash == HashPassword(text, hash); - } - - /// - /// Encode a byte array using bcrypt's slightly-modified base64 encoding scheme. Note that this - /// is *not* compatible with the standard MIME-base64 encoding. - /// - /// Thrown when one or more arguments have unsupported or - /// illegal values. - /// The byte array to encode. - /// The number of bytes to encode. - /// Base64-encoded string. - private static string EncodeBase64(byte[] byteArray, int length) - { - if (length <= 0 || length > byteArray.Length) - throw new ArgumentException("Invalid length", "length"); - - int off = 0; - StringBuilder rs = new StringBuilder(); - while (off < length) - { - int c1 = byteArray[off++] & 0xff; - rs.Append(_Base64Code[(c1 >> 2) & 0x3f]); - c1 = (c1 & 0x03) << 4; - if (off >= length) - { - rs.Append(_Base64Code[c1 & 0x3f]); - break; - } - int c2 = byteArray[off++] & 0xff; - c1 |= (c2 >> 4) & 0x0f; - rs.Append(_Base64Code[c1 & 0x3f]); - c1 = (c2 & 0x0f) << 2; - if (off >= length) - { - rs.Append(_Base64Code[c1 & 0x3f]); - break; - } - c2 = byteArray[off++] & 0xff; - c1 |= (c2 >> 6) & 0x03; - rs.Append(_Base64Code[c1 & 0x3f]); - rs.Append(_Base64Code[c2 & 0x3f]); - } - return rs.ToString(); - } - - /// - /// Decode a string encoded using bcrypt's base64 scheme to a byte array. Note that this is *not* - /// compatible with the standard MIME-base64 encoding. - /// - /// Thrown when one or more arguments have unsupported or - /// illegal values. - /// The string to decode. - /// The maximum bytes to decode. - /// The decoded byte array. - private static byte[] DecodeBase64(string encodedstring, int maximumBytes) - { - int position = 0, - sourceLength = encodedstring.Length, - outputLength = 0; - - if (maximumBytes <= 0) - throw new ArgumentException("Invalid maximum bytes value", "maximumBytes"); - - // TODO: update to use a List - it's only ever 16 bytes, so it's not a big deal - StringBuilder rs = new StringBuilder(); - while (position < sourceLength - 1 && outputLength < maximumBytes) - { - int c1 = Char64(encodedstring[position++]); - int c2 = Char64(encodedstring[position++]); - if (c1 == -1 || c2 == -1) - break; - - rs.Append((char)((c1 << 2) | ((c2 & 0x30) >> 4))); - if (++outputLength >= maximumBytes || position >= sourceLength) - break; - - int c3 = Char64(encodedstring[position++]); - if (c3 == -1) - break; - - rs.Append((char)(((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2))); - if (++outputLength >= maximumBytes || position >= sourceLength) - break; - - int c4 = Char64(encodedstring[position++]); - rs.Append((char)(((c3 & 0x03) << 6) | c4)); - - ++outputLength; - } - - byte[] ret = new byte[outputLength]; - for (position = 0; position < outputLength; position++) - ret[position] = (byte)rs[position]; - return ret; - } - - /// - /// Look up the 3 bits base64-encoded by the specified character, range-checking against - /// conversion table. - /// - /// The base64-encoded value. - /// The decoded value of x. - private static int Char64(char character) - { - if (character < 0 || character > _Index64.Length) - return -1; - return _Index64[character]; - } - /// Blowfish encipher a single 64-bit block encoded as two 32-bit halves. /// An array containing the two 32-bit half blocks. /// The position in the array of the blocks. @@ -759,59 +429,11 @@ private void EKSKey(byte[] saltBytes, byte[] inputBytes) } } - /// Perform the central hashing step in the bcrypt scheme. - /// Thrown when one or more arguments have unsupported or - /// illegal values. - /// The input byte array to hash. - /// The salt byte array to hash with. - /// The binary logarithm of the number of rounds of hashing to apply. - /// A byte array containing the hashed result. - private byte[] CryptRaw(byte[] inputBytes, byte[] saltBytes, int logRounds) - { - uint[] cdata = new uint[_BfCryptCiphertext.Length]; - Array.Copy(_BfCryptCiphertext, cdata, _BfCryptCiphertext.Length); - int clen = cdata.Length; - - if (logRounds < 4 || logRounds > 31) - throw new ArgumentException("Bad number of rounds", "logRounds"); - - if (saltBytes.Length != BCRYPT_SALT_LEN) - throw new ArgumentException("Bad salt Length", "saltBytes"); - - uint rounds = 1u << logRounds; - Debug.Assert(rounds > 0, "Rounds must be > 0"); // We overflowed rounds at 31 - added safety check - - InitializeKey(); - EKSKey(saltBytes, inputBytes); - - for (int i = 0; i < rounds; i++) - { - Key(inputBytes); - Key(saltBytes); - } - - for (int i = 0; i < 64; i++) - { - for (int j = 0; j < (clen >> 1); j++) - Encipher(cdata, j << 1); - } - - byte[] ret = new byte[clen * 4]; - for (int i = 0, j = 0; i < clen; i++) - { - ret[j++] = (byte)((cdata[i] >> 24) & 0xff); - ret[j++] = (byte)((cdata[i] >> 16) & 0xff); - ret[j++] = (byte)((cdata[i] >> 8) & 0xff); - ret[j++] = (byte)(cdata[i] & 0xff); - } - return ret; - } - /** * Compatibility with new OpenBSD function. * Ported from SSHJ library (https://github.com/hierynomus/sshj) */ - public void Hash(byte[] hpass, byte[] hsalt, byte[] output) + private void Hash(byte[] hpass, byte[] hsalt, byte[] output) { InitializeKey(); EKSKey(hsalt, hpass); @@ -924,29 +546,5 @@ private byte[] AppendArrays(byte[] b1, params byte[][] others) return result; } - - /// Exception for signalling parse errors. - public class SaltParseException : Exception - { - /// Default constructor. - public SaltParseException() - { - } - - /// Initializes a new instance of . - /// The message. - public SaltParseException(string message) - : base(message) - { - } - - /// Initializes a new instance of . - /// The message. - /// The inner exception. - public SaltParseException(string message, Exception innerException) - : base(message, innerException) - { - } - } } }