#if defined(_WIN32)
#include <windows.h>
#include <bcrypt.h>
#include <limits.h>
#include <stdint.h>
#include <string.h>
#include "hymma/hlm.h"
#include "hlm_ed25519.h"
#include "hlm_sha256.h"
#pragma comment(lib, "bcrypt.lib")
#ifndef NT_SUCCESS
#define NT_SUCCESS(s) (((NTSTATUS)(s)) >= 0)
#endif
static NTSTATUS cng_sha256(const uint8_t *msg, size_t msg_len,
UCHAR out[HLM_SHA256_DIGEST_SIZE])
{
BCRYPT_ALG_HANDLE halg = NULL;
BCRYPT_HASH_HANDLE hh = NULL;
NTSTATUS st = BCryptOpenAlgorithmProvider(&halg, BCRYPT_SHA256_ALGORITHM,
NULL, 0);
if (!NT_SUCCESS(st)) return st;
st = BCryptCreateHash(halg, &hh, NULL, 0, NULL, 0, 0);
if (NT_SUCCESS(st)) {
st = BCryptHashData(hh, (PUCHAR)msg, (ULONG)msg_len, 0);
if (NT_SUCCESS(st))
st = BCryptFinishHash(hh, out, HLM_SHA256_DIGEST_SIZE, 0);
BCryptDestroyHash(hh);
}
BCryptCloseAlgorithmProvider(halg, 0);
return st;
}
static int cng_verify_rsa(const hlm_public_key *key,
const uint8_t *msg, size_t msg_len,
const uint8_t *sig, size_t sig_len)
{
BCRYPT_ALG_HANDLE alg = NULL;
BCRYPT_KEY_HANDLE hkey = NULL;
NTSTATUS st;
int result = 0;
UCHAR hash[HLM_SHA256_DIGEST_SIZE];
DWORD hash_len = 0;
BCRYPT_PKCS1_PADDING_INFO pad;
UCHAR blob[8192];
BCRYPT_RSAKEY_BLOB hdr;
size_t blob_len;
blob_len = sizeof(hdr) + key->u.rsa.e_len + key->u.rsa.n_len;
if (blob_len > sizeof(blob)) return HLM_E_ARG;
hdr.Magic = BCRYPT_RSAPUBLIC_MAGIC;
hdr.BitLength = (ULONG)(key->u.rsa.n_len * 8);
hdr.cbPublicExp = (ULONG)key->u.rsa.e_len;
hdr.cbModulus = (ULONG)key->u.rsa.n_len;
hdr.cbPrime1 = 0;
hdr.cbPrime2 = 0;
memcpy(blob, &hdr, sizeof(hdr));
memcpy(blob + sizeof(hdr), key->u.rsa.e, key->u.rsa.e_len);
memcpy(blob + sizeof(hdr) + key->u.rsa.e_len,
key->u.rsa.n, key->u.rsa.n_len);
st = BCryptOpenAlgorithmProvider(&alg, BCRYPT_RSA_ALGORITHM, NULL, 0);
if (!NT_SUCCESS(st)) return HLM_E_UNSUPPORTED_ALG;
st = BCryptImportKeyPair(alg, NULL, BCRYPT_RSAPUBLIC_BLOB, &hkey,
blob, (ULONG)blob_len, 0);
if (!NT_SUCCESS(st)) goto done;
st = cng_sha256(msg, msg_len, hash);
if (!NT_SUCCESS(st)) goto done;
hash_len = HLM_SHA256_DIGEST_SIZE;
pad.pszAlgId = BCRYPT_SHA256_ALGORITHM;
st = BCryptVerifySignature(hkey, &pad, hash, hash_len,
(PUCHAR)sig, (ULONG)sig_len, BCRYPT_PAD_PKCS1);
result = NT_SUCCESS(st) ? 1 : 0;
done:
if (hkey) BCryptDestroyKey(hkey);
if (alg) BCryptCloseAlgorithmProvider(alg, 0);
return result;
}
static int cng_verify_ecdsa(const hlm_public_key *key,
const uint8_t *msg, size_t msg_len,
const uint8_t *sig, size_t sig_len)
{
BCRYPT_ALG_HANDLE alg = NULL;
BCRYPT_KEY_HANDLE hkey = NULL;
NTSTATUS st;
int result = 0;
UCHAR hash[HLM_SHA256_DIGEST_SIZE];
UCHAR blob[sizeof(BCRYPT_ECCKEY_BLOB) + 64];
BCRYPT_ECCKEY_BLOB hdr;
if (sig_len != 64) return 0;
hdr.dwMagic = BCRYPT_ECDSA_PUBLIC_P256_MAGIC;
hdr.cbKey = 32;
memcpy(blob, &hdr, sizeof(hdr));
memcpy(blob + sizeof(hdr), key->u.ec.x, 32);
memcpy(blob + sizeof(hdr) + 32, key->u.ec.y, 32);
st = BCryptOpenAlgorithmProvider(&alg, BCRYPT_ECDSA_P256_ALGORITHM, NULL, 0);
if (!NT_SUCCESS(st)) return HLM_E_UNSUPPORTED_ALG;
st = BCryptImportKeyPair(alg, NULL, BCRYPT_ECCPUBLIC_BLOB, &hkey,
blob, sizeof(blob), 0);
if (!NT_SUCCESS(st)) goto done;
st = cng_sha256(msg, msg_len, hash);
if (!NT_SUCCESS(st)) goto done;
st = BCryptVerifySignature(hkey, NULL, hash, HLM_SHA256_DIGEST_SIZE,
(PUCHAR)sig, (ULONG)sig_len, 0);
result = NT_SUCCESS(st) ? 1 : 0;
done:
if (hkey) BCryptDestroyKey(hkey);
if (alg) BCryptCloseAlgorithmProvider(alg, 0);
return result;
}
static int cng_verify(void *user, const hlm_public_key *key,
const uint8_t *msg, size_t msg_len,
const uint8_t *sig, size_t sig_len)
{
(void)user;
#if SIZE_MAX > ULONG_MAX
if (msg_len > ULONG_MAX || sig_len > ULONG_MAX) return 0;
#endif
switch (key->alg) {
case HLM_ALG_RS256:
return cng_verify_rsa(key, msg, msg_len, sig, sig_len);
case HLM_ALG_ES256:
return cng_verify_ecdsa(key, msg, msg_len, sig, sig_len);
case HLM_ALG_EDDSA:
if (sig_len != 64) return 0;
return hlm_ed25519_verify(key->u.ed25519.pub, msg, msg_len, sig);
default:
return HLM_E_UNSUPPORTED_ALG;
}
}
hlm_crypto hlm_crypto_cng(void)
{
hlm_crypto c;
c.verify = cng_verify;
c.user = 0;
return c;
}
#endif