Skip to content

Commit

Permalink
HKDF implementation (dotnet/corefx#42567)
Browse files Browse the repository at this point in the history
* HKDF implementation

* Fix CreateMacProvider on OSX

* apply review feedback

* improve error message in case of test failure


Commit migrated from dotnet/corefx@c14fc56
  • Loading branch information
krwq authored Nov 14, 2019
1 parent ad6c91b commit 32388df
Show file tree
Hide file tree
Showing 13 changed files with 892 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@ internal sealed class HashProviderCng : HashProvider
//
// - "key" activates MAC hashing if present. If null, this HashProvider performs a regular old hash.
//
public HashProviderCng(string hashAlgId, byte[] key)
public HashProviderCng(string hashAlgId, byte[] key) : this(hashAlgId, key, isHmac: key != null)
{
}

internal HashProviderCng(string hashAlgId, ReadOnlySpan<byte> key, bool isHmac)
{
BCryptOpenAlgorithmProviderFlags dwFlags = BCryptOpenAlgorithmProviderFlags.None;
if (key != null)
if (isHmac)
{
_key = key.CloneByteArray();
_key = key.ToArray();
dwFlags |= BCryptOpenAlgorithmProviderFlags.BCRYPT_ALG_HANDLE_HMAC_FLAG;
}

Expand Down Expand Up @@ -63,7 +67,6 @@ public HashProviderCng(string hashAlgId, byte[] key)
throw Interop.BCrypt.CreateCryptographicException(ntStatus);
_hashSize = hashSize;
}
return;
}

public sealed override unsafe void AppendHashData(ReadOnlySpan<byte> source)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ internal partial class Interop
{
internal partial class BCrypt
{
internal static NTSTATUS BCryptCreateHash(SafeBCryptAlgorithmHandle hAlgorithm, out SafeBCryptHashHandle phHash, IntPtr pbHashObject, int cbHashObject, ReadOnlySpan<byte> secret, int cbSecret, BCryptCreateHashFlags dwFlags)
{
return BCryptCreateHash(hAlgorithm, out phHash, pbHashObject, cbHashObject, ref MemoryMarshal.GetReference(secret), cbSecret, dwFlags);
}

[DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)]
internal static extern NTSTATUS BCryptCreateHash(SafeBCryptAlgorithmHandle hAlgorithm, out SafeBCryptHashHandle phHash, IntPtr pbHashObject, int cbHashObject, [In, Out] byte[] pbSecret, int cbSecret, BCryptCreateHashFlags dwFlags);
private static extern NTSTATUS BCryptCreateHash(SafeBCryptAlgorithmHandle hAlgorithm, out SafeBCryptHashHandle phHash, IntPtr pbHashObject, int cbHashObject, ref byte pbSecret, int cbSecret, BCryptCreateHashFlags dwFlags);

[Flags]
internal enum BCryptCreateHashFlags : int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,15 @@ public partial struct ECPoint
public byte[] X;
public byte[] Y;
}
public static class HKDF
{
public static byte[] Extract(HashAlgorithmName hashAlgorithmName, byte[] ikm, byte[] salt = null) { throw null; }
public static int Extract(HashAlgorithmName hashAlgorithmName, ReadOnlySpan<byte> ikm, ReadOnlySpan<byte> salt, Span<byte> prk) { throw null; }
public static byte[] Expand(HashAlgorithmName hashAlgorithmName, byte[] prk, int outputLength, byte[] info = null) { throw null; }
public static void Expand(HashAlgorithmName hashAlgorithmName, ReadOnlySpan<byte> prk, Span<byte> output, ReadOnlySpan<byte> info) { throw null; }
public static byte[] DeriveKey(HashAlgorithmName hashAlgorithmName, byte[] ikm, int outputLength, byte[] salt = null, byte[] info = null) { throw null; }
public static void DeriveKey(HashAlgorithmName hashAlgorithmName, ReadOnlySpan<byte> ikm, Span<byte> output, ReadOnlySpan<byte> salt, ReadOnlySpan<byte> info) { throw null; }
}
public partial class HMACMD5 : System.Security.Cryptography.HMAC
{
public HMACMD5() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,45 @@ namespace Internal.Cryptography
//
internal sealed class HMACCommon
{
public HMACCommon(string hashAlgorithmId, byte[] key, int blockSize)
public HMACCommon(string hashAlgorithmId, byte[] key, int blockSize) : this(hashAlgorithmId, blockSize)
{
ChangeKey(key);
}

internal HMACCommon(string hashAlgorithmId, ReadOnlySpan<byte> key, int blockSize) : this(hashAlgorithmId, blockSize)
{
// note: will not set ActualKey if key size is smaller or equal than blockSize
// this is to avoid extra allocation. ActualKey can still be used if key is generated.
// Otherwise the ReadOnlySpan overload would actually be slower than byte array overload.
ChangeKey(key);
}

private HMACCommon(string hashAlgorithmId, int blockSize)
{
Debug.Assert(!string.IsNullOrEmpty(hashAlgorithmId));
Debug.Assert(blockSize > 0 || blockSize == -1);

_hashAlgorithmId = hashAlgorithmId;
_blockSize = blockSize;
ChangeKey(key);
}

public int HashSizeInBits => _hMacProvider.HashSizeInBytes * 8;

public void ChangeKey(byte[] key)
{
ActualKey = ChangeKeyImpl(key) ?? key;
}

internal void ChangeKey(ReadOnlySpan<byte> key)
{
// note: does not set key when it's smaller than blockSize
ActualKey = ChangeKeyImpl(key);
}

private byte[] ChangeKeyImpl(ReadOnlySpan<byte> key)
{
byte[] modifiedKey = null;

// If _blockSize is -1 the key isn't going to be extractable by the object holder,
// so there's no point in recalculating it in managed code.
if (key.Length > _blockSize && _blockSize > 0)
Expand All @@ -40,16 +65,16 @@ public void ChangeKey(byte[] key)
{
_lazyHashProvider = HashProviderDispenser.CreateHashProvider(_hashAlgorithmId);
}
_lazyHashProvider.AppendHashData(key, 0, key.Length);
key = _lazyHashProvider.FinalizeHashAndReset();
_lazyHashProvider.AppendHashData(key);
modifiedKey = _lazyHashProvider.FinalizeHashAndReset();
}

HashProvider oldHashProvider = _hMacProvider;
_hMacProvider = null;
oldHashProvider?.Dispose(true);
_hMacProvider = HashProviderDispenser.CreateMacProvider(_hashAlgorithmId, key);

ActualKey = key;
return modifiedKey;
}

// The actual key used for hashing. This will not be the same as the original key passed to ChangeKey() if the original key exceeded the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public static HashProvider CreateHashProvider(string hashAlgorithmId)
throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId));
}

public static HashProvider CreateMacProvider(string hashAlgorithmId, byte[] key)
public static HashProvider CreateMacProvider(string hashAlgorithmId, ReadOnlySpan<byte> key)
{
switch (hashAlgorithmId)
{
Expand Down Expand Up @@ -62,9 +62,9 @@ private sealed class AppleHmacProvider : HashProvider

public override int HashSizeInBytes { get; }

internal AppleHmacProvider(Interop.AppleCrypto.PAL_HashAlgorithm algorithm, byte[] key)
internal AppleHmacProvider(Interop.AppleCrypto.PAL_HashAlgorithm algorithm, ReadOnlySpan<byte> key)
{
_key = key.CloneByteArray();
_key = key.ToArray();
int hashSizeInBytes = 0;
_ctx = Interop.AppleCrypto.HmacCreate(algorithm, ref hashSizeInBytes);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public static HashProvider CreateHashProvider(string hashAlgorithmId)
throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId));
}

public static unsafe HashProvider CreateMacProvider(string hashAlgorithmId, byte[] key)
public static unsafe HashProvider CreateMacProvider(string hashAlgorithmId, ReadOnlySpan<byte> key)
{
switch (hashAlgorithmId)
{
Expand Down Expand Up @@ -121,18 +121,17 @@ private sealed class HmacHashProvider : HashProvider
private readonly int _hashSize;
private SafeHmacCtxHandle _hmacCtx;

public HmacHashProvider(IntPtr algorithmEvp, byte[] key)
public HmacHashProvider(IntPtr algorithmEvp, ReadOnlySpan<byte> key)
{
Debug.Assert(algorithmEvp != IntPtr.Zero);
Debug.Assert(key != null);

_hashSize = Interop.Crypto.EvpMdSize(algorithmEvp);
if (_hashSize <= 0 || _hashSize > Interop.Crypto.EVP_MAX_MD_SIZE)
{
throw new CryptographicException();
}

_hmacCtx = Interop.Crypto.HmacCreate(ref MemoryMarshal.GetReference(new Span<byte>(key)), key.Length, algorithmEvp);
_hmacCtx = Interop.Crypto.HmacCreate(ref MemoryMarshal.GetReference(key), key.Length, algorithmEvp);
Interop.Crypto.CheckValidOpenSslHandle(_hmacCtx);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ public static HashProvider CreateHashProvider(string hashAlgorithmId)
return new HashProviderCng(hashAlgorithmId, null);
}

public static HashProvider CreateMacProvider(string hashAlgorithmId, byte[] key)
public static HashProvider CreateMacProvider(string hashAlgorithmId, ReadOnlySpan<byte> key)
{
return new HashProviderCng(hashAlgorithmId, key);
return new HashProviderCng(hashAlgorithmId, key, isHmac: true);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -330,4 +330,10 @@
<data name="Cryptography_WriteEncodedValue_OneValueAtATime" xml:space="preserve">
<value>The input to WriteEncodedValue must represent a single encoded value with no trailing data.</value>
</data>
<data name="Cryptography_Prk_TooSmall" xml:space="preserve">
<value>The pseudo-random key length must be {0} bytes.</value>
</data>
<data name="Cryptography_Okm_TooLarge" xml:space="preserve">
<value>Output keying material length can be at most {0} bytes (255 * hash length).</value>
</data>
</root>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>$(DefineConstants);INTERNAL_ASYMMETRIC_IMPLEMENTATIONS</DefineConstants>
Expand Down Expand Up @@ -45,6 +45,7 @@
<Compile Include="System\Security\Cryptography\ECDsa.Xml.cs" />
<Compile Include="System\Security\Cryptography\ECParameters.cs" />
<Compile Include="System\Security\Cryptography\ECPoint.cs" />
<Compile Include="System\Security\Cryptography\HKDF.cs" />
<Compile Include="System\Security\Cryptography\MaskGenerationMethod.cs" />
<Compile Include="System\Security\Cryptography\MD5.cs" />
<Compile Include="System\Security\Cryptography\SHA1.cs" />
Expand Down Expand Up @@ -726,6 +727,6 @@
<Reference Include="System.Runtime.Numerics" />
</ItemGroup>
<ItemGroup>
<None Include="@(AsnXml)" />
<None Include="@(AsnXml)" />
</ItemGroup>
</Project>
Loading

0 comments on commit 32388df

Please sign in to comment.