[ILASM] Add wrappers for platform-specific SHA-256 implementations#109559
[ILASM] Add wrappers for platform-specific SHA-256 implementations#109559amanasifkhalid merged 13 commits intodotnet:mainfrom
Conversation
|
Tagging subscribers to this area: @JulieLeeMSFT |
|
@jkotas could you PTAL? Thanks! |
src/coreclr/md/enc/sha256.cpp
Outdated
| { | ||
| CC_SHA256_CTX ctx = {{ 0 }}; | ||
|
|
||
| if (!CC_SHA256_Init(&ctx)) |
There was a problem hiding this comment.
Apple has a one-shot you can use too. It would look something like this:
HRESULT Sha256Hash(BYTE* pSrc, DWORD srcSize, BYTE* pDst, DWORD dstSize)
{
if (dstSize < CC_SHA256_DIGEST_LENGTH)
{
return E_FAIL;
}
CC_SHA256(pSrc, (CC_LONG)srcSize, pDst);
return S_OK;
}If there is an actual need for it, you can copy to a temp buffer like Windows and Linux do.
src/coreclr/md/enc/sha256.cpp
Outdated
| goto cleanup; | ||
| } | ||
|
|
||
| memcpy(pDst, hash, min(hashLength, dstSize)); |
There was a problem hiding this comment.
Why do we use min here? Is there a real scenario where dstSize is not big enough to hold the whole SHA-256 and it needs to be truncated?
There was a problem hiding this comment.
We could move the temp buffer logic to the caller
I think it would make more sense.
|
cc @bartonjs This change is borrowing distro agnostic OpenSSL loading logic from System.Security.Cryptography.Native for ildasm |
src/coreclr/ilasm/sha256.h
Outdated
|
|
||
| return S_OK; | ||
| } | ||
| #elif defined(__linux__) |
There was a problem hiding this comment.
This can be just #else as CryptoNative_EvpDigestOneShot is available on any Unix.
src/coreclr/ilasm/CMakeLists.txt
Outdated
| sha256.h | ||
| ) | ||
|
|
||
| if(CLR_CMAKE_TARGET_LINUX) |
There was a problem hiding this comment.
| if(CLR_CMAKE_TARGET_LINUX) | |
| if(CLR_CMAKE_TARGET_UNIX AND NOT CLR_CMAKE_TARGET_APPLE) |
src/coreclr/ilasm/CMakeLists.txt
Outdated
| ) | ||
| endif(CLR_CMAKE_TARGET_WIN32) | ||
|
|
||
| if(CLR_CMAKE_TARGET_LINUX) |
There was a problem hiding this comment.
| if(CLR_CMAKE_TARGET_LINUX) | |
| if(CLR_CMAKE_TARGET_UNIX AND NOT CLR_CMAKE_TARGET_APPLE) |
src/coreclr/ilasm/sha256.h
Outdated
| { | ||
| BCryptDestroyHash(hashHandle); | ||
| } | ||
| if(NULL != algHandle) |
There was a problem hiding this comment.
| if(NULL != algHandle) | |
| if (NULL != algHandle) |
src/coreclr/ilasm/sha256.h
Outdated
| { | ||
| goto cleanup; | ||
| } | ||
| if (hashLength != 32) |
There was a problem hiding this comment.
I am not sure why we need to check this - we do not have the same check for other OSes. SHA256 is defined as returning 32 bytes. If the crypto provider returns something else here, something is seriously broken.
Co-authored-by: Jan Kotas <[email protected]>
src/coreclr/ilasm/sha256.h
Outdated
| inline bool IsOpenSslAvailable() | ||
| { | ||
| return CryptoNative_OpenSslAvailable() != 0; | ||
| } | ||
|
|
||
| inline HRESULT Sha256Hash(BYTE* pSrc, DWORD srcSize, BYTE* pDst, DWORD dstSize) | ||
| { | ||
| if (!IsOpenSslAvailable() || CryptoNative_EnsureOpenSslInitialized() || (dstSize < 32)) |
There was a problem hiding this comment.
nit: perhaps a bit simpler this way:
| inline bool IsOpenSslAvailable() | |
| { | |
| return CryptoNative_OpenSslAvailable() != 0; | |
| } | |
| inline HRESULT Sha256Hash(BYTE* pSrc, DWORD srcSize, BYTE* pDst, DWORD dstSize) | |
| { | |
| if (!IsOpenSslAvailable() || CryptoNative_EnsureOpenSslInitialized() || (dstSize < 32)) | |
| inline HRESULT Sha256Hash(BYTE* pSrc, DWORD srcSize, BYTE* pDst, DWORD dstSize) | |
| { | |
| if (CryptoNative_OpenSslAvailable() == 0 || CryptoNative_EnsureOpenSslInitialized() != 0 || (dstSize < 32)) |
There was a problem hiding this comment.
I agree it looks a bit redundant in its current form. The intent behind IsOpenSslAvailable is to allow ILAsm to disable features based on OpenSSL availability, without leaking the CryptoNative API surface to it. Including it in this PR is probably premature, though I will want something like it when ILAsm starts using these wrappers.
There was a problem hiding this comment.
I thought I saw a comment from Jan K that said that we should treat OpenSSL here as required as it is for the platform... which is to say "it's required for 'Linux'" (which includes OpenBSD/NetBSD/etc).
If you're taking it as required, you can drop the call to CryptoNative_OpenSslAvailable; CryptoNative_EnsureOpenSslInitialized will do the same dlopen that Available does, it just fails (via assert) if the library isn't found.
If you're taking it as optional, then using OpenSslAvailable is correct.
There was a problem hiding this comment.
I suppose it makes sense to delegate the responsibility of checking for OpenSSL availability to the caller, if we're going to provide IsOpenSslAvailable anyway.
There was a problem hiding this comment.
If you just call CryptoNative_EnsureOpenSslInitialized and nothing else, CryptoNative_EnsureOpenSslInitialized is going to print "No usable version of libssl was found" and abort if there is not openssl available on the machine.
I think it is good enough. Explicitly calling OpenSslAvailable makes sense only if you would like to provide a different error message or handle the error situation differently.
src/coreclr/ilasm/sha256.h
Outdated
| return E_FAIL; | ||
| } | ||
|
|
||
| DWORD hashLength = 0; |
There was a problem hiding this comment.
| DWORD hashLength = 0; | |
| uint32_t hashLength = 0; |
src/coreclr/ilasm/sha256.h
Outdated
| DWORD hashLength = 0; | ||
| DWORD resultLength = 0; | ||
|
|
||
| NTSTATUS status = BCryptOpenAlgorithmProvider(&algHandle, BCRYPT_SHA256_ALGORITHM, NULL, 0); | ||
|
|
||
| if (!NT_SUCCESS(status)) | ||
| { | ||
| goto cleanup; | ||
| } | ||
|
|
||
| status = BCryptGetProperty(algHandle, BCRYPT_HASH_LENGTH, (PBYTE)&hashLength, sizeof(hashLength), &resultLength, 0); | ||
|
|
||
| if (!NT_SUCCESS(status)) | ||
| { | ||
| goto cleanup; | ||
| } | ||
|
|
||
| assert(hashLength == 32); | ||
| status = BCryptCreateHash(algHandle, &hashHandle, NULL, 0, NULL, 0, 0); | ||
|
|
||
| if (!NT_SUCCESS(status)) | ||
| { | ||
| goto cleanup; | ||
| } | ||
|
|
||
| status = BCryptHashData(hashHandle, pSrc, srcSize, 0); | ||
|
|
||
| if (!NT_SUCCESS(status)) | ||
| { | ||
| goto cleanup; | ||
| } | ||
|
|
||
| status = BCryptFinishHash(hashHandle, pDst, hashLength, 0); |
There was a problem hiding this comment.
| DWORD hashLength = 0; | |
| DWORD resultLength = 0; | |
| NTSTATUS status = BCryptOpenAlgorithmProvider(&algHandle, BCRYPT_SHA256_ALGORITHM, NULL, 0); | |
| if (!NT_SUCCESS(status)) | |
| { | |
| goto cleanup; | |
| } | |
| status = BCryptGetProperty(algHandle, BCRYPT_HASH_LENGTH, (PBYTE)&hashLength, sizeof(hashLength), &resultLength, 0); | |
| if (!NT_SUCCESS(status)) | |
| { | |
| goto cleanup; | |
| } | |
| assert(hashLength == 32); | |
| status = BCryptCreateHash(algHandle, &hashHandle, NULL, 0, NULL, 0, 0); | |
| if (!NT_SUCCESS(status)) | |
| { | |
| goto cleanup; | |
| } | |
| status = BCryptHashData(hashHandle, pSrc, srcSize, 0); | |
| if (!NT_SUCCESS(status)) | |
| { | |
| goto cleanup; | |
| } | |
| status = BCryptFinishHash(hashHandle, pDst, hashLength, 0); | |
| DWORD resultLength = 0; | |
| NTSTATUS status = BCryptOpenAlgorithmProvider(&algHandle, BCRYPT_SHA256_ALGORITHM, NULL, 0); | |
| if (!NT_SUCCESS(status)) | |
| { | |
| goto cleanup; | |
| } | |
| status = BCryptCreateHash(algHandle, &hashHandle, NULL, 0, NULL, 0, 0); | |
| if (!NT_SUCCESS(status)) | |
| { | |
| goto cleanup; | |
| } | |
| status = BCryptHashData(hashHandle, pSrc, srcSize, 0); | |
| if (!NT_SUCCESS(status)) | |
| { | |
| goto cleanup; | |
| } | |
| status = BCryptFinishHash(hashHandle, pDst, dstSize, 0); |
src/coreclr/ilasm/sha256.h
Outdated
| #ifdef _WIN32 | ||
| inline HRESULT Sha256Hash(BYTE* pSrc, DWORD srcSize, BYTE* pDst, DWORD dstSize) | ||
| { | ||
| if (dstSize < 32) |
There was a problem hiding this comment.
Should this check for exact match?
Taking output buffer, filling part of it, and not telling the caller how much of it was filled is odd. The typical patterns for output buffers are:
- Take output buffer and fill it completely, fail if it is not possible to fill it completely
- Take output buffer and return how much of it was filled
src/coreclr/ilasm/sha256.h
Outdated
|
|
||
| inline HRESULT Sha256Hash(BYTE* pSrc, DWORD srcSize, BYTE* pDst, DWORD dstSize) | ||
| { | ||
| if (!IsOpenSslAvailable() || CryptoNative_EnsureOpenSslInitialized() || (dstSize < 32)) |
There was a problem hiding this comment.
| if (!IsOpenSslAvailable() || CryptoNative_EnsureOpenSslInitialized() || (dstSize < 32)) | |
| if (CryptoNative_EnsureOpenSslInitialized() || (dstSize < 32)) |
CryptoNative_OpenSslAvailable call is unnecessary here. It is done by CryptoNative_EnsureOpenSslInitialized
Co-authored-by: Adeel Mujahid <[email protected]>
Co-authored-by: Adeel Mujahid <[email protected]>
|
Thanks for the reviews! |
Part of #108627. I'm splitting this out of #109091 to expedite review, as the crypto part of the ILAsm determinism work seems to be in decent shape. The intent is to consume this API surface in ILAsm to produce checksums, though I imagine these wrappers could be useful elsewhere.