Last active
October 29, 2017 20:17
-
-
Save OpBug/4460d322178887a4fcb6590d4a059fff to your computer and use it in GitHub Desktop.
C# implementation of HMAC-based key derivation functionality, HKDF.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
/// <summary> | |
/// Specifies the name of a cryptographic HMAC algorithm. | |
/// </summary> | |
public struct HMACAlgorithmName : IEquatable<HMACAlgorithmName> | |
{ | |
#region " Members " | |
private readonly string _name; | |
#endregion | |
#region " Properties " | |
/// <summary> | |
/// Gets the underlying string representation of the algorithm name. | |
/// </summary> | |
/// <value>The string representation of the algorithm name, or null or <see cref="String.Empty"/> if no hash algorithm is available.</value> | |
public string Name | |
{ | |
get { return _name; } | |
} | |
/// <summary> | |
/// Gets a HMAC algorithm name that represents "MD5". | |
/// </summary> | |
/// <value>A HMAC algorithm name that represents "MD5".</value> | |
public static HMACAlgorithmName HMACMD5 | |
{ | |
get { return new HMACAlgorithmName("HMACMD5"); } | |
} | |
/// <summary> | |
/// Gets a HMAC algorithm name that represents "HMACRIPEMD160". | |
/// </summary> | |
/// <value>A HMAC algorithm name that represents "HMACRIPEMD160".</value> | |
public static HMACAlgorithmName HMACRIPEMD160 | |
{ | |
get { return new HMACAlgorithmName("HMACRIPEMD160"); } | |
} | |
/// <summary> | |
/// Gets a HMAC algorithm name that represents "HMACSHA1". | |
/// </summary> | |
/// <value>A HMAC algorithm name that represents "HMACSHA1".</value> | |
public static HMACAlgorithmName HMACSHA1 | |
{ | |
get { return new HMACAlgorithmName("HMACSHA1"); } | |
} | |
/// <summary> | |
/// Gets a HMAC algorithm name that represents "HMACSHA256". | |
/// </summary> | |
/// <value>A HMAC algorithm name that represents "HMACSHA256".</value> | |
public static HMACAlgorithmName HMACSHA256 | |
{ | |
get { return new HMACAlgorithmName("HMACSHA256"); } | |
} | |
/// <summary> | |
/// Gets a HMAC algorithm name that represents "HMACSHA384". | |
/// </summary> | |
/// <value>A HMAC algorithm name that represents "HMACSHA384".</value> | |
public static HMACAlgorithmName HMACSHA384 | |
{ | |
get { return new HMACAlgorithmName("HMACSHA384"); } | |
} | |
/// <summary> | |
/// Gets a HMAC algorithm name that represents "HMACSHA512". | |
/// </summary> | |
/// <value>A HMAC algorithm name that represents "HMACSHA512".</value> | |
public static HMACAlgorithmName HMACSHA512 | |
{ | |
get { return new HMACAlgorithmName("HMACSHA512"); } | |
} | |
/// <summary> | |
/// Gets a HMAC algorithm name that represents "MACTripleDES". | |
/// </summary> | |
/// <value>A HMAC algorithm name that represents "MACTripleDES".</value> | |
public static HMACAlgorithmName MACTripleDES | |
{ | |
get { return new HMACAlgorithmName("MACTripleDES"); } | |
} | |
#endregion | |
#region " Constructor " | |
/// <summary> | |
/// Initializes a new instance of the <see cref="HMACAlgorithmName"/> structure with a custom name. | |
/// </summary> | |
/// <param name="name">The custom HMAC algorithm name.</param> | |
public HMACAlgorithmName(string name) | |
{ | |
_name = name; | |
} | |
#endregion | |
#region " Overrides " | |
/// <summary> | |
/// Returns a value that indicates whether the current instance and a specified object are equal. | |
/// </summary> | |
/// <param name="obj">The object to compare with the current instance.</param> | |
/// <returns>true if obj is a <see cref="HMACAlgorithmName"/> object and its <see cref="Name"/> property is equal to that of the current instance. The comparison is ordinal and case-sensitive.</returns> | |
public override bool Equals(object obj) | |
{ | |
if (!(obj is HMACAlgorithmName)) | |
{ | |
return false; | |
} | |
return Equals((HMACAlgorithmName)obj); | |
} | |
/// <summary> | |
/// Returns the hash code for the current instance. | |
/// </summary> | |
/// <returns>The hash code for the current instance, or 0 if no name value was supplied to the <see cref="HMACAlgorithmName"/> constructor.</returns> | |
public override int GetHashCode() | |
{ | |
if (_name == null) | |
{ | |
return 0; | |
} | |
return _name.GetHashCode(); | |
} | |
/// <summary> | |
/// Returns the string representation of the current <see cref="HMACAlgorithmName"/> instance. | |
/// </summary> | |
/// <returns>The string representation of the current <see cref="HMACAlgorithmName"/> instance.</returns> | |
public override string ToString() | |
{ | |
return _name ?? string.Empty; | |
} | |
#endregion | |
#region " IEquatable " | |
/// <summary> | |
/// Determines whether two specified <see cref="HMACAlgorithmName"/> objects are equal. | |
/// </summary> | |
/// <param name="left">The first object to compare.</param> | |
/// <param name="right">The second object to compare.</param> | |
/// <returns>true if both left and right have the same <see cref="Name"/> value; otherwise, false.</returns> | |
public static bool operator ==(HMACAlgorithmName left, HMACAlgorithmName right) | |
{ | |
return left.Equals(right); | |
} | |
/// <summary> | |
/// Determines whether two specified <see cref="HMACAlgorithmName"/> objects are not equal. | |
/// </summary> | |
/// <param name="left">The first object to compare.</param> | |
/// <param name="right">The second object to compare.</param> | |
/// <returns>true if both left and right do not have the same <see cref="Name"/> value; otherwise, false.</returns> | |
public static bool operator !=(HMACAlgorithmName left, HMACAlgorithmName right) | |
{ | |
return !(left == right); | |
} | |
/// <summary> | |
/// Returns a value that indicates whether two <see cref="HMACAlgorithmName"/> instances are equal. | |
/// </summary> | |
/// <param name="other">The object to compare with the current instance.</param> | |
/// <returns>true if the <see cref="Name"/> property of other is equal to that of the current instance. The comparison is ordinal and case-sensitive.</returns> | |
public bool Equals(HMACAlgorithmName other) | |
{ | |
return _name == other.Name; | |
} | |
#endregion | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Security.Cryptography; | |
/// <summary> | |
/// Implements HMAC-based key derivation functionality, HKDF, using the provided HMAC algorithm. | |
/// </summary> | |
public sealed class Rfc5869DeriveBytes | |
{ | |
#region " Properties " | |
/// <summary> | |
/// Gets the max length of bytes that can be derived by the operation. | |
/// </summary> | |
/// <value>The max length that can be derived by the operation.</value> | |
public int MaxLength | |
{ | |
get { return _hashLength * 255; } | |
} | |
#endregion | |
#region " Members " | |
private HMAC _hmac; | |
private int _hashLength; | |
#endregion | |
#region " Constructor " | |
/// <summary> | |
/// Initializes a new instance of the <see cref="Rfc5869DeriveBytes"/> class using a hash algorithim, a key, and salt to derive the internal key. | |
/// </summary> | |
/// <param name="hashAlgorithm">The HMAC hash implementation to use.</param> | |
/// <param name="key">The key used to derive the internal key.</param> | |
/// <param name="salt">The salt used to derive the internal key.</param> | |
/// <exception cref="ArgumentNullException">The hashAlgorithim or key is null.</exception> | |
public Rfc5869DeriveBytes(HMACAlgorithmName hashAlgorithm, byte[] key, byte[] salt = null) | |
{ | |
if (hashAlgorithm == null) | |
{ | |
throw new ArgumentNullException(nameof(hashAlgorithm)); | |
} | |
Initialize(HMAC.Create(hashAlgorithm.Name), key, salt); | |
} | |
/// <summary> | |
/// Initializes a new instance of the <see cref="Rfc5869DeriveBytes"/> class using an HMAC, a key, and salt to derive the internal key. | |
/// </summary> | |
/// <param name="hmac">The HMAC implementation to use.</param> | |
/// <param name="key">The key used to derive the internal key.</param> | |
/// <param name="salt">The salt used to derive the internal key.</param> | |
/// <exception cref="ArgumentNullException">The hmac or key is null.</exception> | |
public Rfc5869DeriveBytes(HMAC hmac, byte[] key, byte[] salt = null) | |
{ | |
if (hmac == null) | |
{ | |
throw new ArgumentNullException(nameof(hmac)); | |
} | |
Initialize(hmac, key, salt); | |
} | |
private void Initialize(HMAC hmac, byte[] key, byte[] salt) | |
{ | |
if (key == null) | |
{ | |
throw new ArgumentNullException(nameof(key)); | |
} | |
_hmac = hmac; | |
_hmac.Key = salt ?? new byte[_hmac.HashSize / 8]; | |
_hmac.Key = _hmac.ComputeHash(key); | |
_hashLength = _hmac.HashSize / 8; | |
} | |
#endregion | |
#region " Derivation " | |
/// <summary> | |
/// Returns the pseudo-random key for this object. | |
/// </summary> | |
/// <param name="cb">The number of pseudo-random key bytes to generate.</param> | |
/// <param name="info">Optional context and application specific information.</param> | |
/// <returns>A byte array filled with pseudo-random key bytes.</returns> | |
/// <exception cref="ArgumentOutOfRangeException"><paramref name="cb"/> is out of range. This parameter requires a non-negative number less than <see cref="MaxLength"/>.</exception> | |
public byte[] GetBytes(int cb, byte[] info = null) | |
{ | |
if (cb < 0 || cb > this.MaxLength) | |
{ | |
throw new ArgumentOutOfRangeException(nameof(cb)); | |
} | |
if (info == null) | |
{ | |
info = new byte[0]; | |
} | |
byte[] hash = new byte[0]; | |
byte[] output = new byte[cb]; | |
byte[] buffer = new byte[info.Length + 1]; | |
int bytesToCopy = 0; | |
int bytesCopied = 0; | |
int bufferSize = _hashLength + info.Length + 1; | |
byte counter = 1; | |
while (bytesCopied < cb) | |
{ | |
Buffer.BlockCopy(hash, 0, buffer, 0, hash.Length); | |
Buffer.BlockCopy(info, 0, buffer, hash.Length, info.Length); | |
buffer[buffer.Length - 1] = counter++; | |
hash = _hmac.ComputeHash(buffer); | |
if (buffer.Length < bufferSize) | |
{ | |
buffer = new byte[bufferSize]; | |
} | |
bytesToCopy = Math.Min(hash.Length, cb - bytesCopied); | |
Buffer.BlockCopy(hash, 0, output, bytesCopied, bytesToCopy); | |
bytesCopied += bytesToCopy; | |
} | |
return output; | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment