Signing with CryptoAPI using Certificate and Detached Private Key

Use of separated certificate and private key might be confusing without understanding how parts or CryptoAPI are related one to another. Apparently, CryptSignMessage and friends require private key in order to create a digital signature.

It is not a problem when private key resides right in the signing certificate (such as, for example, imported with PFXImportCertStore):

CRYPT_SIGN_MESSAGE_PARA Parameters;
ZeroMemory(&Parameters, sizeof Parameters);
Parameters.cbSize = sizeof Parameters;
Parameters.dwMsgEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
Parameters.pSigningCert = CertificateContext; // <<--- Certificate with Private Key
// ...

Things get more complicated when the certificate does not contain the private key. API’s error is CRYPT_E_NO_KEY_PROPERTY which is described as follows:

“The pSigningCert in *pSignPara does not have a CERT_KEY_PROV_INFO_PROP_ID or CERT_KEY_CONTEXT_PROP_ID property.”

CERT_KEY_PROV_INFO_PROP_ID is a property to be defined with certificate context and it expects a structure of type CRYPT_KEY_PROV_INFO. The other one, CERT_KEY_CONTEXT_PROP_ID takes a CERT_KEY_CONTEXT structure.

Even though described as “The CRYPT_KEY_PROV_INFO structure contains information about a key container within a cryptographic service provider (CSP).” and “The CERT_KEY_CONTEXT structure contains data associated with a CERT_KEY_CONTEXT_PROP_ID property.” respectively, it is not well clear to see where the key is attached exactly.

The missing part is that the private key is not something to be provided explicitly. Instead, the key is to be pre-loaded into provider and then this provider is associated with CERT_KEY_CONTEXT structure, which goes as a property value, which in turn is added to signing certificate context, whcih finally gets used for creating a digital signature.

__E(CryptStringToBinary(sPrivateKeyBlob, (DWORD) wcslen(sPrivateKeyBlob), CRYPT_STRING_BASE64_ANY, pnData, &nDataSize, NULL, NULL));
// ...
CCryptProvider Provider;
__E(Provider.AcquireContext(NULL, NULL, PROV_RSA_FULL, 0));
CCryptKey Key;
Key.Attach(Provider.ImportKey(pnData, nDataSize));
__E(Key);
// ...
CERT_KEY_CONTEXT KeyContext;
ZeroMemory(&KeyContext, sizeof KeyContext);
KeyContext.cbSize = sizeof KeyContext;
KeyContext.hCryptProv = Provider; // <<--- Here we go, ooh
KeyContext.dwKeySpec = AT_SIGNATURE;
CertificateContext.SetProperty(CERT_KEY_CONTEXT_PROP_ID, &KeyContext);

Utility Clearance: Matrox DSX SDK Capabilities

The tool gathers and formats capture capabilities of hardware accessible via Matrox DSX SDK:

Matrox DSX SDK is a feature-rich development toolkit that enables Matrox’s industry-leading DSX hardware capabilities. OEMs use this hardware and software to build complete solutions that ensure 24/7 on-air reliability in a broadcast environment. Matrox DSX SDK customers have access to unlimited premium support from experienced applications engineers, personalized training courses, design review programs, and feature-customization to meet their specific needs.

Specifically, I used it a lot (X.AVCio, MXO2 series and other) to identify inputs, their properties, genlock, auto-detection of video signal.

Continue reading →

How to create Virtual Webcam in Windows 10?

(this is a re-post from StackOverflow)

Virtual webcam is typically a software only implementation that application discover as if it is a device with physical representation. The mentioned applications use APIs to work with web cameras and ability to extend the APIs and add your own video source is the way to create a virtual web camera.

In Windows there are a few APIs to consume video sources: Video for Windows, DirectShow, Media Foundation (in chronological order).

Video for Windows is not really extensible and limited in capabilities overall. It will see a virtual device if you provide a kernel mode driver for a virtual camera.

DirectShow is the API used by most video capture enabled Windows applications and it is present in all Windows versions including Windows 10 (except just Windows RT). Then it’s perfectly extensible and in most cases the term “virtual webcam” refers to DirectShow virtual webcam. Methods to create DirectShow virtual webcam discussed in many StackOverflow questions remain perfectly valid for Windows 10, for applications that implement video capture using DirectShow:

DirectShow samples were removed from Windows SDK but you can still find them in older releases:

If you provide a kernel mode driver for video camera device (your virtual webcam through custom kernel driver), DirectShow would also see it just like other video APIs.

Media Foundation is a supposed successor of DirectShow but its video capture capabilities in the part of extensibility simply do not exist. Microsoft decided to not allow custom video sources application would be able to discover the same way as web cameras. Due to Media Foundation complexity, and overhead and overall unfriendliness it is used by modest amount of applications. To implement a virtual webcam for Media Foundation application you again, like in case of Video for Windows, have to implement a kernel mode driver.

See also:

06-Jun-2022 Update: See the post below for, finally, proper & official solution for software/virtual video camera challenge in Windows 11:

Windows 11 Virtual Video Camera

Calling convention violator broke streaming loop pretty far away

A really nasty problem coming from MainConcept AVC/H.264 SDK Encoder was destroying media streaming pipeline. SDK is somewhat old (9.7.9.5738) and the problem might be already fixed, or might be not. The problem is a good example of how a small bug could become a big pain.

The problem was coming up in 64-bit Release builds only. Win32 build? OK. Debug build where you can step things through? No problem.

The bug materialized in GDCL MP4 Demultiplexer filter streaming (Demultiplexer filter in the pipeline below) generating media samples with incorrect time stamps.

Pipeline

Initial start and stop time are okay, and further go as _I64_MIN (incorrect).

Clipbrd3

The problem appears to be SSE optimization and x64 calling convention related. This explains why it’s only 64-bit Release build suffering from the issue. MS compiler decided to use XMM7 register for dRate variable in this code fragment:

REFERENCE_TIME tStart, tStop;
double dRate;
m_pParser->GetSeekingParams(&tStart, &tStop, &dRate);

[...]

for(; ; )
{
    [...]

    tSampleStart = REFERENCE_TIME(tSampleStart / dRate);
    tSampleEnd = REFERENCE_TIME(tSampleEnd / dRate);

dRate is the only floating point thing here and it’s clear why the compiler optimized the variable into register: no other floating point activity around.

However sample delivery goes pretty deep into other functions and modules reaching MainConcept H.264 encoder. One of its functions is violating x64 calling convention and does not preserve XMM6+ register values. OOPS! Everything is about working right, but after media sample delivery dRate value is destroyed and further media samples receive incorrect time stamps.

It is not really a problem of MP4 demultiplexer, of course, however media sample delivery might involve a long delivery chain where any violator would break streaming loop. In the same time, it is not really a big expense to de-optimize the floating point math in the demultiplexer for those a few time stamp adjustment operations. A volatile specifier breaks compiler optimization and makes the loop resistant to SSE2 register violators:

// HOTFIX: Volatile specifier is not really necessary here but it fixes a nasty problem with MainConcept AVC SDK violating x64 calling convention;
//         MS compiler might choose to keep dRate in XMM6 register and the value would be destroyed by the violating call leading to incorrect 
//         further streaming (wrong time stamps)
volatile DOUBLE dRate;
m_pParser->GetSeekingParams(&tStart, &tStop, (DOUBLE*) &dRate);

This makes H.264 this build of encoding SDK unstable and the problem is hopefully already fixed. The SDK indeed gave other troubles on specific architectures leading to undefined behavior.

Certificate Enrollment API Adventures

IX509PrivateKey::Export exports key data from COM object, MSDN:

strExportType [in]

A BSTR value that specifies how the private key is exported.

If the key was created by using a CNG KSP (Key Storage Provider), you can specify one of the values allowed by the pszBlobType parameter in the NCryptExportKey function.

If the key was created by using a CryptoAPI CSP (Cryptographic Service Provider), you can specify one of the following values from the Bcrypt.h header file included with Wincrypt.h.
BCRYPT_PUBLIC_KEY_BLOB – Exports only the public portion of the private key.
BCRYPT_
PRIVATE_KEY_BLOB – Exports the entire private key.

Well this is actually accurate, however trying to make it…

CComPtr<IX509PrivateKey> pPrivateKey;
// ...
CComBSTR sPublicKeyBlob, sPrivateKeyBlob;
ATLENSURE_SUCCEEDED(pPrivateKey->Export(BCRYPT_PUBLIC_KEY_BLOB, XCN_CRYPT_STRING_BASE64, &sPublicKeyBlob));
ATLENSURE_SUCCEEDED(pPrivateKey->Export(BCRYPT_PRIVATE_KEY_BLOB, XCN_CRYPT_STRING_BASE64, &sPrivateKeyBlob));

… the both calls result in E_POINTER (0x80004003). The first argument is not just LPCWSTR. It has to be BSTR and the API is checking the length of the value using SysStringLen. One has to wrap the argument to provide it as BSTR:

CComPtr<IX509PrivateKey> pPrivateKey;
// ...
CComBSTR sPublicKeyBlob, sPrivateKeyBlob;
ATLENSURE_SUCCEEDED(pPrivateKey->Export(CComBSTR(BCRYPT_PUBLIC_KEY_BLOB), XCN_CRYPT_STRING_BASE64, &sPublicKeyBlob));
ATLENSURE_SUCCEEDED(pPrivateKey->Export(CComBSTR(BCRYPT_PRIVATE_KEY_BLOB), XCN_CRYPT_STRING_BASE64, &sPrivateKeyBlob));

Another problem is related to IObjectId::InitializeFromAlgorithmName.

strAlgorithmName [in]

A BSTR variable that contains the name. You can specify a name, or an OID in dotted decimal format. The method verifies that the format is consistent with the ASN.1 X.208 standard. For more information about CNG algorithm names, see CNG Algorithm Identifiers.

MSDN offers choices to provide a string or OID. Unfortunately, OID does not work and fails on internal argument validation with E_INVALIDARG (0x80070057).

ATLENSURE_SUCCEEDED(pObjectId->InitializeFromAlgorithmName(XCN_CRYPT_HASH_ALG_OID_GROUP_ID, 
    XCN_CRYPT_OID_INFO_PUBKEY_ANY, AlgorithmFlagsNone, 
        CComBSTR(L"SHA256"))); //CComBSTR(szOID_RSA_SHA256RSA)));

FFmpeg error LNK2019: unresolved external symbol ff_reshuffle_raw_rgb…

Building a slim version of FFmpeg (specifically: MOV muxer without AVI muxer) from latest source, an error around ff_reshuffle_raw_rgb is breaking the build:

movenc.o : error LNK2019: unresolved external symbol ff_reshuffle_raw_rgb referenced in function mov_write_packet
libavformat/avformat-v-57.dll : fatal error LNK1120: 1 unresolved externals
library.mak:111: recipe for target 'libavformat/avformat-v-57.dll' failed
make: *** [libavformat/avformat-v-57.dll] Error 96

Not a big a deal, it is just  a small bug there:

 libavformat/Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavformat/Makefile b/libavformat/Makefile
index 3b32907..6ae4327 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -268,7 +268,7 @@ OBJS-$(CONFIG_MMF_DEMUXER)               += mmf.o
 OBJS-$(CONFIG_MMF_MUXER)                 += mmf.o rawenc.o
 OBJS-$(CONFIG_MOV_DEMUXER)               += mov.o mov_chan.o replaygain.o
 OBJS-$(CONFIG_MOV_MUXER)                 += movenc.o avc.o hevc.o \
-                                            movenchint.o mov_chan.o rtp.o movenccenc.o
+                                            movenchint.o mov_chan.o rtp.o movenccenc.o rawutils.o
 OBJS-$(CONFIG_MP2_MUXER)                 += mp3enc.o rawenc.o id3v2enc.o
 OBJS-$(CONFIG_MP3_DEMUXER)               += mp3dec.o replaygain.o
 OBJS-$(CONFIG_MP3_MUXER)                 += mp3enc.o rawenc.o id3v2enc.o