/// <inheritdoc /> public override long Seek(long offset, SeekOrigin origin) { long pos; switch (origin) { case SeekOrigin.Begin: pos = offset; break; case SeekOrigin.Current: pos = Position + offset; break; case SeekOrigin.End: pos = Length + offset; break; default: throw Exceptions.ArgumentOutOfRange("origin", "Unknown seek type."); } if (pos < 0) { throw Exceptions.Argument("offset", "Can't seek before the stream start."); } Position = pos; return(pos); }
/// <inheritdoc /> public override string GenerateSalt(CrypterOptions options) { Check.Null("options", options); switch (options.GetValue(CrypterOption.Variant, LdapCrypterVariant.SSha)) { case LdapCrypterVariant.Crypt: Crypter crypter = options.GetValue <Crypter>(LdapCrypterOption.Crypter); if (crypter == null) { throw Exceptions.Argument("LdapCrypterOption.Crypter", "Crypter not set. Did you intend Crypter.TraditionalDes (the slappasswd default)?"); } CrypterOptions crypterOptions = options.GetValue(LdapCrypterOption.CrypterOptions, CrypterOptions.None); return("{CRYPT}" + crypter.GenerateSalt(crypterOptions)); case LdapCrypterVariant.SSha: return("{SSHA}" + Convert.ToBase64String(Security.GenerateRandomBytes(8))); case LdapCrypterVariant.SMD5: return("{SMD5}" + Convert.ToBase64String(Security.GenerateRandomBytes(8))); case LdapCrypterVariant.Sha: return("{SHA}"); case LdapCrypterVariant.MD5: return("{MD5}"); case LdapCrypterVariant.Cleartext: return("{CLEARTEXT}"); default: throw Exceptions.ArgumentOutOfRange("CrypterOption.Variant", "Unknown variant."); } }
/// <inheritdoc /> public override string Crypt(byte[] password, string salt) { Check.Null("password", password); Check.Null("salt", salt); Match match = _regex.Match(salt); if (!match.Success) { throw Exceptions.Argument("salt", "Invalid salt."); } byte[] roundsBytes = null, saltBytes = null, crypt = null, input = null; try { string roundsString = match.Groups["rounds"].Value; roundsBytes = Base64Encoding.UnixMD5.GetBytes(roundsString); int roundsValue = (int)BitPacking.UInt24FromLEBytes(roundsBytes, 0); string saltString = match.Groups["salt"].Value; saltBytes = Base64Encoding.UnixMD5.GetBytes(saltString); int saltValue = (int)BitPacking.UInt24FromLEBytes(saltBytes, 0); input = new byte[8]; int length = ByteArray.NullTerminatedLength(password, password.Length); for (int m = 0; m < length; m += 8) { if (m != 0) { using (DesCipher cipher = DesCipher.Create(input)) { cipher.Encipher(input, 0, input, 0); } } for (int n = 0; n < 8 && n < length - m; n++) { // DES Crypt ignores the high bit of every byte. input[n] ^= (byte)(password[m + n] << 1); } } using (DesCipher cipher = DesCipher.Create(input)) { crypt = new byte[8]; cipher.Crypt(crypt, 0, roundsValue, saltValue); } return("_" + roundsString + saltString + Base64Encoding.UnixCrypt.GetString(crypt)); } finally { Security.Clear(roundsBytes); Security.Clear(saltBytes); Security.Clear(crypt); Security.Clear(input); } }
/// <inheritdoc /> public override string Crypt(byte[] password, string salt) { Check.Null("password", password); Check.Null("salt", salt); Match match = _regex.Match(salt); if (!match.Success) { throw Exceptions.Argument("salt", "Invalid salt."); } byte[] saltBytes = null, crypt = null; try { string roundsString = match.Groups["rounds"].Value; int rounds = Base64Encoding.UnixMD5.GetValue(roundsString[0]); if (rounds < MinRounds || rounds > MaxRounds) { throw Exceptions.ArgumentOutOfRange("salt", "Invalid number of rounds."); } string prefixString = match.Groups["prefix"].Value; bool sha512 = prefixString == "$S$"; string saltString = match.Groups["salt"].Value; saltBytes = Encoding.ASCII.GetBytes(saltString); HashAlgorithm A; if (sha512) { A = System.Security.Cryptography.SHA512.Create(); } else { A = System.Security.Cryptography.MD5.Create(); } crypt = Crypt(password, saltBytes, rounds, A); string hashString = Base64Encoding.UnixMD5.GetString(crypt); if (sha512) { hashString = hashString.Substring(0, 43); } string result = prefixString + roundsString + saltString + hashString; return(result); } finally { Security.Clear(saltBytes); Security.Clear(crypt); } }
/// <inheritdoc /> public override string Crypt(byte[] password, string salt) { Check.Null("password", password); Check.Null("salt", salt); if (string.IsNullOrEmpty(salt)) { return(Crypt(password)); } Match match = _regex.Match(salt); if (!match.Success) { var hash = Crypt(password); var matchUnsalted = string.Equals(hash, salt, StringComparison.InvariantCultureIgnoreCase); if (matchUnsalted) { return(hash); } else { throw Exceptions.Argument("salt", "Invalid salt."); } } byte[] prefixBytes = null, saltBytes = null, formattedKey = null, truncatedSalt = null, crypt = null; try { string prefixString = match.Groups["prefix"].Value; prefixBytes = Encoding.ASCII.GetBytes(prefixString); string saltString = match.Groups["salt"].Value; saltBytes = Encoding.ASCII.GetBytes(saltString); formattedKey = FormatKey(password); truncatedSalt = ByteArray.TruncateAndCopy(saltBytes, 8); crypt = Crypt(formattedKey, truncatedSalt, prefixBytes, System.Security.Cryptography.MD5.Create()); string result = prefixString + saltString + '$' + Base64Encoding.UnixMD5.GetString(crypt); return(result); } finally { Security.Clear(prefixBytes); Security.Clear(saltBytes); Security.Clear(formattedKey); Security.Clear(truncatedSalt); Security.Clear(crypt); } }
/// <summary> /// Searches for a crypt algorithm compatible with the specified crypted password or prefix. /// </summary> /// <param name="cryptedPassword">The crypted password or prefix.</param> /// <returns>A compatible crypt algorithm.</returns> /// <exception cref="ArgumentException">No compatible crypt algorithm was found.</exception> public Crypter GetCrypter(string cryptedPassword) { Crypter crypter; if (TryGetCrypter(cryptedPassword, out crypter)) { return(crypter); } else { throw Exceptions.Argument("cryptedPassword", "Unsupported algorithm."); } }
/// <inheritdoc /> public override string Crypt(byte[] password, string salt) { Check.Null("password", password); Check.Null("salt", salt); Match match = GetRegex().Match(salt); if (!match.Success) { throw Exceptions.Argument("salt", "Invalid salt."); } string roundsString = match.Groups["rounds"].Value; bool roundsStringPresent = roundsString.Length != 0; int rounds = roundsStringPresent ? int.Parse(roundsString) : 5000; //int requestedRounds = rounds; // PHP tests indicate the rounds string is NOT preserved if the count is outside spec. if (rounds < MinRounds) { rounds = MinRounds; } if (rounds > MaxRounds) { rounds = MaxRounds; } byte[] saltBytes = null, formattedKey = null, truncatedSalt = null, crypt = null; try { string saltString = match.Groups["salt"].Value; saltBytes = Encoding.ASCII.GetBytes(saltString); formattedKey = FormatKey(password); truncatedSalt = ByteArray.TruncateAndCopy(saltBytes, 16); crypt = Crypt(formattedKey, truncatedSalt, rounds, CreateHashAlgorithm()); string result = CryptPrefix + (roundsStringPresent ? string.Format("rounds={0}$", rounds) : "") + Encoding.ASCII.GetString(truncatedSalt) + '$' + Base64Encoding.UnixMD5.GetString(crypt); return(result); } finally { Security.Clear(saltBytes); Security.Clear(formattedKey); Security.Clear(truncatedSalt); Security.Clear(crypt); } }
/// <summary> /// Reads from the derived key stream. /// </summary> /// <param name="count">The number of bytes to read.</param> /// <returns>Bytes from the derived key stream.</returns> public byte[] Read(int count) { NBitcoin.Crypto.Internal.Check.Range("count", count, 0, int.MaxValue); byte[] buffer = new byte[count]; int bytes = Read(buffer, 0, count); if (bytes < count) { throw Exceptions.Argument("count", "Can only return {0} bytes.", bytes); } return(buffer); }
/// <summary> /// Throws an exception if the value is incompatible with this option. /// </summary> /// <param name="value">The value to check.</param> public void CheckValue(object value) { if (value is null) { throw Exceptions.ArgumentNull(nameof(value)); } if (!ValueType.IsAssignableFrom(value.GetType())) { throw Exceptions.Argument(nameof(value), "Value is incompatible with type {0}.", ValueType); } OnCheckValue(value); }
/// <inheritdoc /> public override long Seek(long offset, SeekOrigin origin) { var pos = origin switch { SeekOrigin.Begin => offset, SeekOrigin.Current => Position + offset, SeekOrigin.End => Length + offset, _ => throw Exceptions.ArgumentOutOfRange("origin", "Unknown seek type."), }; if (pos < 0) { throw Exceptions.Argument("offset", "Can't seek before the stream start."); } Position = pos; return(pos); }
public Pbkdf2(KeyedHashAlgorithm hmacAlgorithm, byte[] salt, int iterations) { NBitcoin.Crypto.Internal.Check.Null("hmacAlgorithm", hmacAlgorithm); NBitcoin.Crypto.Internal.Check.Null("salt", salt); NBitcoin.Crypto.Internal.Check.Length("salt", salt, 0, int.MaxValue - 4); NBitcoin.Crypto.Internal.Check.Range("iterations", iterations, 1, int.MaxValue); if (hmacAlgorithm.HashSize == 0 || hmacAlgorithm.HashSize % 8 != 0) { throw Exceptions.Argument("hmacAlgorithm", "Unsupported hash size."); } int hmacLength = hmacAlgorithm.HashSize / 8; _saltBuffer = new byte[salt.Length + 4]; Array.Copy(salt, _saltBuffer, salt.Length); _iterations = iterations; _hmacAlgorithm = hmacAlgorithm; _digest = new byte[hmacLength]; _digestT1 = new byte[hmacLength]; }
/// <summary> /// Gets the value of an option, if the option is set, or a specified default value otherwise. /// </summary> /// <typeparam name="T">The type of the option's value.</typeparam> /// <param name="key">The key of the option.</param> /// <param name="defaultValue">The default value if the option is not set.</param> /// <returns>The option's value.</returns> public T GetValue <T>(CrypterOptionKey key, T defaultValue) { object value; if (!TryGetValue(key, out value)) { return(defaultValue); } try { return((T)value); } catch (InvalidCastException) { throw Exceptions.Argument(key.Description, "Expected type {0}.", typeof(T)); } }
/// <inheritdoc /> public override string Crypt(byte[] password, string salt) { Check.Null("password", password); Check.Null("salt", salt); Match match = _regex.Match(salt); if (!match.Success) { throw Exceptions.Argument("salt", "Invalid salt."); } byte[] crypt = null, input = null; try { string saltString = FilterSalt(match.Groups["salt"].Value); input = new byte[8]; int length = ByteArray.NullTerminatedLength(password, input.Length); Array.Copy(password, input, Math.Min(length, input.Length)); // DES Crypt ignores the high bit of every byte. for (int n = 0; n < 8; n++) { input[n] <<= 1; } using (DesCipher cipher = DesCipher.Create(input)) { int saltValue = Base64Encoding.UnixCrypt.GetValue(saltString[0]) << 0 | Base64Encoding.UnixCrypt.GetValue(saltString[1]) << 6; crypt = new byte[8]; cipher.Crypt(crypt, 0, 25, saltValue); } return(saltString + Base64Encoding.UnixCrypt.GetString(crypt)); } finally { Security.Clear(crypt); Security.Clear(input); } }
/// <inheritdoc /> public override string Crypt(byte[] password, string salt) { Check.Null("password", password); Check.Null("salt", salt); Match match = _regex.Match(salt); if (!match.Success) { throw Exceptions.Argument("salt", "Invalid salt."); } byte[] saltBytes = null, formattedKey = null, crypt = null; try { string prefixString = match.Groups["prefix"].Value; bool compatible = prefixString == "$2x$"; int rounds = int.Parse(match.Groups["rounds"].Value); if (rounds < MinRounds || rounds > MaxRounds) { throw Exceptions.ArgumentOutOfRange("salt", "Invalid number of rounds."); } saltBytes = Base64Encoding.Blowfish.GetBytes(match.Groups["salt"].Value); formattedKey = FormatKey(password); crypt = BlowfishCipher.BCrypt(formattedKey, saltBytes, rounds, compatible ? EksBlowfishKeyExpansionFlags.EmulateCryptBlowfishSignExtensionBug : EksBlowfishKeyExpansionFlags.None); string result = string.Format("{0}{1}${2}{3}", prefixString, rounds.ToString("00"), Base64Encoding.Blowfish.GetString(saltBytes), Base64Encoding.Blowfish.GetString(crypt)); return(result); } finally { Security.Clear(saltBytes); Security.Clear(formattedKey); Security.Clear(crypt); } }
static string SaltedCrypt(HashAlgorithm algorithm, byte[] password, string saltString) { byte[] salt = null; try { if (!TryConvertFromBase64String(saltString, out salt)) { throw Exceptions.Argument("salt", "Bad base-64."); } // If we're under the hash length, assume we only have the salt. int hashLength = algorithm.HashSize / 8; int saltOffset = salt.Length < hashLength ? 0 : hashLength; int saltLength = salt.Length - saltOffset; byte[] saltedHash = new byte[hashLength + saltLength]; try { algorithm.Initialize(); algorithm.TransformBlock(password, 0, password.Length, password, 0); algorithm.TransformBlock(salt, saltOffset, saltLength, salt, saltOffset); algorithm.TransformFinalBlock(new byte[0], 0, 0); Array.Copy(algorithm.Hash, saltedHash, hashLength); Array.Copy(salt, saltOffset, saltedHash, hashLength, saltLength); string crypt = Convert.ToBase64String(saltedHash); return(crypt); } finally { algorithm.Clear(); Security.Clear(saltedHash); } } finally { Security.Clear(salt); } }
/// <inheritdoc /> public override string Crypt(byte[] password, string salt) { Check.Null("password", password); Check.Null("salt", salt); Match match = _regex.Match(salt); if (!match.Success) { throw Exceptions.Argument("salt", "Invalid salt."); } byte[] prefixBytes = null, saltBytes = null, formattedKey = null, truncatedSalt = null, crypt = null; try { string prefixString = match.Groups["prefix"].Value; prefixBytes = Encoding.ASCII.GetBytes(prefixString); string saltString = match.Groups["salt"].Value; saltBytes = Encoding.ASCII.GetBytes(saltString); formattedKey = FormatKey(password); truncatedSalt = ByteArray.TruncateAndCopy(saltBytes, 8); crypt = Crypt(formattedKey, truncatedSalt, prefixBytes, System.Security.Cryptography.MD5.Create()); string result = prefixString + saltString + '$' + Base64Encoding.UnixMD5.GetString(crypt); return(result); } finally { Security.Clear(prefixBytes); Security.Clear(saltBytes); Security.Clear(formattedKey); Security.Clear(truncatedSalt); Security.Clear(crypt); } }
/// <summary> /// Defines a binary-to-text encoding. /// Additional decode characters let you add aliases, and a filter callback can be used /// to make decoding case-insensitive among other things. /// </summary> /// <param name="characterSet">The characters of the encoding.</param> /// <param name="msbComesFirst"> /// <c>true</c> to begin with the most-significant bit of each byte. /// Otherwise, the encoding begins with the least-significant bit. /// </param> /// <param name="additionalDecodeCharacters"> /// A dictionary of alias characters, or <c>null</c> if no aliases are desired. /// </param> /// <param name="decodeFilterCallback"> /// A callback to map arbitrary characters onto the characters that can be decoded. /// </param> public BaseEncoding(string characterSet, bool msbComesFirst, IDictionary <char, int> additionalDecodeCharacters, BaseEncodingDecodeFilterCallback decodeFilterCallback) { Check.Null("characterSet", characterSet); if (!BitMath.IsPositivePowerOf2(characterSet.Length)) { throw Exceptions.Argument("characterSet", "Length must be a power of 2."); } if (characterSet.Length > 256) { throw Exceptions.Argument("characterSet", "Character sets with over 256 characters are not supported."); } _bitCount = 31 - BitMath.CountLeadingZeros(characterSet.Length); _bitMask = (1 << _bitCount) - 1; _characters = characterSet; _msbComesFirst = msbComesFirst; _decodeFilterCallback = decodeFilterCallback; _values = additionalDecodeCharacters != null ? new Dictionary <char, int>(additionalDecodeCharacters) : new Dictionary <char, int>(); for (int i = 0; i < characterSet.Length; i++) { char ch = characterSet[i]; if (_values.ContainsKey(ch)) { throw Exceptions.Argument("Duplicate characters are not supported.", "characterSet"); } _values.Add(ch, (byte)i); } }
/// <inheritdoc /> public override string Crypt(byte[] password, string salt) { Check.Null("password", password); Check.Null("salt", salt); Match match; LdapCrypterVariant variant; if (!TryMatch(salt, out match, out variant)) { throw Exceptions.Argument("salt", "Invalid salt."); } string prefixString = match.Groups["prefix"].Value; string saltString = match.Groups["salt"].Value; switch (variant) { case LdapCrypterVariant.Crypt: Crypter crypter; if (!_environment.TryGetCrypter(saltString, out crypter)) { goto default; } if (crypter is LdapCrypter) { throw Exceptions.Argument("salt", "LDAP {CRYPT} tried to use an LDAP scheme."); } return(prefixString + crypter.Crypt(password, saltString)); case LdapCrypterVariant.SSha512: return(prefixString + SaltedCrypt(SHA512.Create(), password, saltString)); case LdapCrypterVariant.SSha384: return(prefixString + SaltedCrypt(SHA384.Create(), password, saltString)); case LdapCrypterVariant.SSha256: return(prefixString + SaltedCrypt(SHA256.Create(), password, saltString)); case LdapCrypterVariant.SSha: return(prefixString + SaltedCrypt(SHA1.Create(), password, saltString)); case LdapCrypterVariant.SMD5: return(prefixString + SaltedCrypt(System.Security.Cryptography.MD5.Create(), password, saltString)); case LdapCrypterVariant.Sha512: return(prefixString + UnsaltedCrypt(SHA512.Create(), password)); case LdapCrypterVariant.Sha384: return(prefixString + UnsaltedCrypt(SHA384.Create(), password)); case LdapCrypterVariant.Sha256: return(prefixString + UnsaltedCrypt(SHA256.Create(), password)); case LdapCrypterVariant.Sha: return(prefixString + UnsaltedCrypt(SHA1.Create(), password)); case LdapCrypterVariant.MD5: return(prefixString + UnsaltedCrypt(System.Security.Cryptography.MD5.Create(), password)); case LdapCrypterVariant.Cleartext: return(prefixString + Encoding.UTF8.GetString(password)); default: throw Exceptions.ArgumentOutOfRange("CrypterOption.Variant", "Unknown variant."); } }
/// <summary> /// Applies the Salsa20 hash function. /// It maps a 16 element input to an output of the same size. /// </summary> /// <param name="rounds">The number of rounds. SCrypt uses 8.</param> /// <param name="input">The input buffer.</param> /// <param name="inputOffset">The offset into the input buffer.</param> /// <param name="output">The output buffer.</param> /// <param name="outputOffset">The offset into the output buffer.</param> public static void Compute(int rounds, uint[] input, int inputOffset, uint[] output, int outputOffset) { if (rounds < 2 || rounds > 20 || (rounds & 1) == 1) { throw Exceptions.Argument("rounds", "Must be even and in the range 2 to 20."); } try { // .NET's bounds checking hurts performance in tight loops like this one. // So, I unroll the array to eliminate it - a 50% speed increase. uint x0 = input[inputOffset + 0]; uint x1 = input[inputOffset + 1]; uint x2 = input[inputOffset + 2]; uint x3 = input[inputOffset + 3]; uint x4 = input[inputOffset + 4]; uint x5 = input[inputOffset + 5]; uint x6 = input[inputOffset + 6]; uint x7 = input[inputOffset + 7]; uint x8 = input[inputOffset + 8]; uint x9 = input[inputOffset + 9]; uint x10 = input[inputOffset + 10]; uint x11 = input[inputOffset + 11]; uint x12 = input[inputOffset + 12]; uint x13 = input[inputOffset + 13]; uint x14 = input[inputOffset + 14]; uint x15 = input[inputOffset + 15]; for (int i = rounds; i > 0; i -= 2) { x4 ^= R(x0 + x12, 7); x8 ^= R(x4 + x0, 9); x12 ^= R(x8 + x4, 13); x0 ^= R(x12 + x8, 18); x9 ^= R(x5 + x1, 7); x13 ^= R(x9 + x5, 9); x1 ^= R(x13 + x9, 13); x5 ^= R(x1 + x13, 18); x14 ^= R(x10 + x6, 7); x2 ^= R(x14 + x10, 9); x6 ^= R(x2 + x14, 13); x10 ^= R(x6 + x2, 18); x3 ^= R(x15 + x11, 7); x7 ^= R(x3 + x15, 9); x11 ^= R(x7 + x3, 13); x15 ^= R(x11 + x7, 18); x1 ^= R(x0 + x3, 7); x2 ^= R(x1 + x0, 9); x3 ^= R(x2 + x1, 13); x0 ^= R(x3 + x2, 18); x6 ^= R(x5 + x4, 7); x7 ^= R(x6 + x5, 9); x4 ^= R(x7 + x6, 13); x5 ^= R(x4 + x7, 18); x11 ^= R(x10 + x9, 7); x8 ^= R(x11 + x10, 9); x9 ^= R(x8 + x11, 13); x10 ^= R(x9 + x8, 18); x12 ^= R(x15 + x14, 7); x13 ^= R(x12 + x15, 9); x14 ^= R(x13 + x12, 13); x15 ^= R(x14 + x13, 18); } output[outputOffset + 0] = input[inputOffset + 0] + x0; x0 = 0; output[outputOffset + 1] = input[inputOffset + 1] + x1; x1 = 0; output[outputOffset + 2] = input[inputOffset + 2] + x2; x2 = 0; output[outputOffset + 3] = input[inputOffset + 3] + x3; x3 = 0; output[outputOffset + 4] = input[inputOffset + 4] + x4; x4 = 0; output[outputOffset + 5] = input[inputOffset + 5] + x5; x5 = 0; output[outputOffset + 6] = input[inputOffset + 6] + x6; x6 = 0; output[outputOffset + 7] = input[inputOffset + 7] + x7; x7 = 0; output[outputOffset + 8] = input[inputOffset + 8] + x8; x8 = 0; output[outputOffset + 9] = input[inputOffset + 9] + x9; x9 = 0; output[outputOffset + 10] = input[inputOffset + 10] + x10; x10 = 0; output[outputOffset + 11] = input[inputOffset + 11] + x11; x11 = 0; output[outputOffset + 12] = input[inputOffset + 12] + x12; x12 = 0; output[outputOffset + 13] = input[inputOffset + 13] + x13; x13 = 0; output[outputOffset + 14] = input[inputOffset + 14] + x14; x14 = 0; output[outputOffset + 15] = input[inputOffset + 15] + x15; x15 = 0; } catch (IndexOutOfRangeException) { // For speed, don't bounds-check until .NET throws from a bounds error. Check.Null("input", input); Check.Bounds("input", input, inputOffset, 16); Check.Null("output", output); Check.Bounds("output", output, outputOffset, 16); throw; } }