Skip to content

PBKDF2 one-shot #48107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 32 commits into from
Feb 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
5f15fdf
Basic implementation of one-shot PBKDF2 for all platforms.
vcsjones Feb 8, 2021
c77b4e7
Use HashAlgorithmName to PAL
vcsjones Feb 10, 2021
1bf9d93
Use psuedo handles if available on the Windows platform.
vcsjones Feb 10, 2021
22ead64
Fix compilation
vcsjones Feb 10, 2021
abe4c36
Undo inadvertent change in ref file.
vcsjones Feb 10, 2021
859112e
Detect Windows 7 instead of relying on the NTSTATUS.
vcsjones Feb 10, 2021
62d0ec2
Loosen restrictions and add deeper tests
vcsjones Feb 10, 2021
d8e5c2d
Fix empty password on macOS.
vcsjones Feb 10, 2021
5ef000d
Permit empty salts
vcsjones Feb 11, 2021
d1c34a3
Fix OpenSSL 1.0 with empty values
vcsjones Feb 11, 2021
a873c76
Rename exported function
vcsjones Feb 11, 2021
eed78a4
Rename Apple exported function
vcsjones Feb 11, 2021
51b77c1
Cleanup native PALs.
vcsjones Feb 11, 2021
61140da
Merge remote-tracking branch 'ms/master' into 24897-pbkdf2-one-shot
vcsjones Feb 12, 2021
0210441
Fix issue with not slicing the rented buffer.
vcsjones Feb 12, 2021
f8d82d3
Use BCryptKeyDerivation on Windows 8+.
vcsjones Feb 15, 2021
ff56305
Cleanup and some refactoring for Windows implementation
vcsjones Feb 16, 2021
c322666
Use throwing UTF8.
vcsjones Feb 16, 2021
8c6d370
Change to follow approved API.
vcsjones Feb 16, 2021
ddfd960
Tests for overlapping input / output buffers.
vcsjones Feb 16, 2021
5a2c91a
Add tests around HMAC block size boundaries.
vcsjones Feb 16, 2021
b82528e
Fix block sizes.
vcsjones Feb 16, 2021
dc72dc3
XML documentation.
vcsjones Feb 16, 2021
3ec9a55
Code review feedback.
vcsjones Feb 17, 2021
be3ca75
Merge remote-tracking branch 'ms/master' into 24897-pbkdf2-one-shot
vcsjones Feb 17, 2021
19faa41
Move high iterations test to OuterLoop.
vcsjones Feb 17, 2021
b98a679
Dispose of handles in error conditions
vcsjones Feb 17, 2021
bc02881
Rename buffer descriptors with clearer prefix
vcsjones Feb 17, 2021
8f00cf6
Refactor input validation.
vcsjones Feb 17, 2021
b2199b1
Test for large passwords and salts.
vcsjones Feb 17, 2021
c78ca1a
Merge remote-tracking branch 'ms/master' into 24897-pbkdf2-one-shot
vcsjones Feb 19, 2021
b881b3a
Use asserts to make what should be pre-validated paths clearer.
vcsjones Feb 19, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Cryptography;

internal static partial class Interop
{
internal static partial class AppleCrypto
{
internal static unsafe void Pbkdf2(
PAL_HashAlgorithm prfAlgorithm,
ReadOnlySpan<byte> password,
ReadOnlySpan<byte> salt,
int iterations,
Span<byte> destination)
{
fixed (byte* pPassword = password)
fixed (byte* pSalt = salt)
fixed (byte* pDestination = destination)
{
int ret = AppleCryptoNative_Pbkdf2(
prfAlgorithm,
pPassword,
password.Length,
pSalt,
salt.Length,
iterations,
pDestination,
destination.Length,
out int ccStatus);

if (ret == 0)
{
throw Interop.AppleCrypto.CreateExceptionForCCError(
ccStatus,
Interop.AppleCrypto.CCCryptorStatus);
}

if (ret != 1)
{
Debug.Fail($"Pbkdf2 failed with invalid input {ret}");
throw new CryptographicException();
}
}
}

[DllImport(Libraries.AppleCryptoNative)]
private static extern unsafe int AppleCryptoNative_Pbkdf2(
PAL_HashAlgorithm prfAlgorithm,
byte* password,
int passwordLen,
byte* salt,
int saltLen,
int iterations,
byte* derivedKey,
int derivedKeyLen,
out int errorCode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,43 @@ internal static int EvpDigestUpdate(SafeEvpMdCtxHandle ctx, ReadOnlySpan<byte> d
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpSha512")]
internal static extern IntPtr EvpSha512();


[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetMaxMdSize")]
private static extern int GetMaxMdSize();

[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_Pbkdf2")]
private static unsafe extern int Pbkdf2(
byte* pPassword,
int passwordLength,
byte* pSalt,
int saltLength,
int iterations,
IntPtr digestEvp,
byte* pDestination,
int destinationLength);

internal static unsafe int Pbkdf2(
ReadOnlySpan<byte> password,
ReadOnlySpan<byte> salt,
int iterations,
IntPtr digestEvp,
Span<byte> destination)
{
fixed (byte* pPassword = password)
fixed (byte* pSalt = salt)
fixed (byte* pDestination = destination)
{
return Pbkdf2(
pPassword,
password.Length,
pSalt,
salt.Length,
iterations,
digestEvp,
pDestination,
destination.Length);
}
}

internal static readonly int EVP_MAX_MD_SIZE = GetMaxMdSize();
}
}
1 change: 1 addition & 0 deletions src/libraries/Common/src/Interop/Windows/BCrypt/Cng.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ internal static class AlgorithmName
public const string Sha256 = "SHA256"; // BCRYPT_SHA256_ALGORITHM
public const string Sha384 = "SHA384"; // BCRYPT_SHA384_ALGORITHM
public const string Sha512 = "SHA512"; // BCRYPT_SHA512_ALGORITHM
public const string Pbkdf2 = "PBKDF2"; // BCRYPT_PBKDF2_ALGORITHM
}

internal static class KeyDerivationFunction
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.InteropServices;

internal partial class Interop
{
internal partial class BCrypt
{
// Pseudo-handles, as defined in bcrypt.h
// TODO: This really should be backed by 'nuint' (see https://github.com/dotnet/roslyn/issues/44110)
public enum BCryptAlgPseudoHandle : uint
{
BCRYPT_MD5_ALG_HANDLE = 0x00000021,
BCRYPT_SHA1_ALG_HANDLE = 0x00000031,
BCRYPT_SHA256_ALG_HANDLE = 0x00000041,
BCRYPT_SHA384_ALG_HANDLE = 0x00000051,
BCRYPT_SHA512_ALG_HANDLE = 0x00000061,
BCRYPT_PBKDF2_ALG_HANDLE = 0x00000331,
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

using Microsoft.Win32.SafeHandles;

internal partial class Interop
{
internal partial class BCrypt
{
[DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)]
internal static extern unsafe NTSTATUS BCryptDeriveKeyPBKDF2(
SafeBCryptAlgorithmHandle hPrf,
byte* pbPassword,
int cbPassword,
byte* pbSalt,
int cbSalt,
ulong cIterations,
byte* pbDerivedKey,
int cbDerivedKey,
uint dwFlags);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

internal partial class Interop
{
internal partial class BCrypt
{
[DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)]
internal static unsafe extern NTSTATUS BCryptGenerateSymmetricKey(
SafeBCryptAlgorithmHandle hAlgorithm,
out SafeBCryptKeyHandle phKey,
IntPtr pbKeyObject,
int cbKeyObject,
byte* pbSecret,
int cbSecret,
uint dwFlags);

[DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)]
internal static unsafe extern NTSTATUS BCryptGenerateSymmetricKey(
nuint hAlgorithm,
out SafeBCryptKeyHandle phKey,
IntPtr pbKeyObject,
int cbKeyObject,
byte* pbSecret,
int cbSecret,
uint dwFlags);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,5 @@ internal partial class BCrypt
{
[DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)]
internal static unsafe extern NTSTATUS BCryptHash(nuint hAlgorithm, byte* pbSecret, int cbSecret, byte* pbInput, int cbInput, byte* pbOutput, int cbOutput);

// Pseudo-handles, as defined in bcrypt.h
// TODO: This really should be backed by 'nuint' (see https://github.com/dotnet/roslyn/issues/44110)
public enum BCryptAlgPseudoHandle : uint
{
BCRYPT_MD5_ALG_HANDLE = 0x00000021,
BCRYPT_SHA1_ALG_HANDLE = 0x00000031,
BCRYPT_SHA256_ALG_HANDLE = 0x00000041,
BCRYPT_SHA384_ALG_HANDLE = 0x00000051,
BCRYPT_SHA512_ALG_HANDLE = 0x00000061,
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

using Microsoft.Win32.SafeHandles;

internal partial class Interop
{
internal partial class BCrypt
{
[DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)]
internal static unsafe extern NTSTATUS BCryptKeyDerivation(
SafeBCryptKeyHandle hKey,
BCryptBufferDesc* pParameterList,
byte* pbDerivedKey,
int cbDerivedKey,
out uint pcbResult,
int dwFlags);
}
}
23 changes: 20 additions & 3 deletions src/libraries/Common/src/Interop/Windows/BCrypt/Interop.Blobs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,27 @@ internal struct BCRYPT_ECCFULLKEY_BLOB
}

/// <summary>
/// NCrypt buffer descriptors
/// NCrypt or BCrypt buffer descriptors
/// </summary>
internal enum NCryptBufferDescriptors : int
internal enum CngBufferDescriptors : int
{
KDF_HASH_ALGORITHM = 0,
KDF_SECRET_PREPEND = 1,
KDF_SECRET_APPEND = 2,
KDF_HMAC_KEY = 3,
KDF_TLS_PRF_LABEL = 4,
KDF_TLS_PRF_SEED = 5,
KDF_SECRET_HANDLE = 6,
KDF_TLS_PRF_PROTOCOL = 7,
KDF_ALGORITHMID = 8,
KDF_PARTYUINFO = 9,
KDF_PARTYVINFO = 10,
KDF_SUPPPUBINFO = 11,
KDF_SUPPPRIVINFO = 12,
KDF_LABEL = 13,
KDF_CONTEXT = 14,
KDF_SALT = 15,
KDF_ITERATION_COUNT = 16,
NCRYPTBUFFER_ECC_CURVE_NAME = 60,
}

Expand All @@ -241,7 +258,7 @@ internal enum NCryptBufferDescriptors : int
internal struct BCryptBuffer
{
internal int cbBuffer; // Length of buffer, in bytes
internal NCryptBufferDescriptors BufferType; // Buffer type
internal CngBufferDescriptors BufferType; // Buffer type
internal IntPtr pvBuffer; // Pointer to buffer
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ internal static SafeNCryptKeyHandle ImportKeyBlob(
descPtr = Marshal.AllocHGlobal(Marshal.SizeOf(desc));
buffPtr = Marshal.AllocHGlobal(Marshal.SizeOf(buff));
buff.cbBuffer = (curveName.Length + 1) * 2; // Add 1 for null terminator
buff.BufferType = Interop.BCrypt.NCryptBufferDescriptors.NCRYPTBUFFER_ECC_CURVE_NAME;
buff.BufferType = Interop.BCrypt.CngBufferDescriptors.NCRYPTBUFFER_ECC_CURVE_NAME;
buff.pvBuffer = safeCurveName.DangerousGetHandle();
Marshal.StructureToPtr(buff, buffPtr, false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ set(NATIVECRYPTO_SOURCES
pal_ecc.c
pal_hmac.c
pal_keyagree.c
pal_keyderivation.c
pal_keychain.c
pal_random.c
pal_rsa.c
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "pal_trust.h"
#include "pal_x509.h"
#include "pal_x509chain.h"
#include "pal_keyderivation.h"

static const Entry s_cryptoAppleNative[] =
{
Expand Down Expand Up @@ -103,6 +104,7 @@ static const Entry s_cryptoAppleNative[] =
DllImportEntry(AppleCryptoNative_X509ChainGetStatusAtIndex)
DllImportEntry(AppleCryptoNative_GetOSStatusForChainStatus)
DllImportEntry(AppleCryptoNative_X509ChainSetTrustAnchorCertificates)
DllImportEntry(AppleCryptoNative_Pbkdf2)
};

EXTERN_C const void* CryptoAppleResolveDllImport(const char* name);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#include "pal_keyderivation.h"

#if !defined(TARGET_IOS) && !defined(TARGET_TVOS)

static int32_t PrfAlgorithmFromHashAlgorithm(PAL_HashAlgorithm hashAlgorithm, CCPseudoRandomAlgorithm* algorithm)
{
if (algorithm == NULL)
return 0;

switch (hashAlgorithm)
{
case PAL_SHA1:
*algorithm = kCCPRFHmacAlgSHA1;
return 1;
case PAL_SHA256:
*algorithm = kCCPRFHmacAlgSHA256;
return 1;
case PAL_SHA384:
*algorithm = kCCPRFHmacAlgSHA384;
return 1;
case PAL_SHA512:
*algorithm = kCCPRFHmacAlgSHA512;
return 1;
default:
*algorithm = 0;
return 0;
}
}

int32_t AppleCryptoNative_Pbkdf2(PAL_HashAlgorithm prfAlgorithm,
const char* password,
int32_t passwordLen,
const uint8_t* salt,
int32_t saltLen,
int32_t iterations,
uint8_t* derivedKey,
uint32_t derivedKeyLen,
int32_t* errorCode)
{
if (errorCode != NULL)
*errorCode = noErr;

if (passwordLen < 0 || saltLen < 0 || iterations < 0 || derivedKey == NULL ||
derivedKeyLen < 0 || errorCode == NULL)
{
return -1;
}

if (salt == NULL && saltLen != 0)
{
return -1;
}

const char* empty = "";

if (password == NULL)
{
if (passwordLen != 0)
{
return -1;
}

// macOS will not accept a null password, but it will accept a zero-length
// password with a valid pointer.
password = empty;
}

CCPseudoRandomAlgorithm prf;

if (!PrfAlgorithmFromHashAlgorithm(prfAlgorithm, &prf))
{
return -2;
}

CCStatus result = CCKeyDerivationPBKDF(kCCPBKDF2, password, passwordLen, salt,
saltLen, prf, iterations, derivedKey, derivedKeyLen);
*errorCode = result;
return result == kCCSuccess ? 1 : 0;
}
#endif
Loading