[Cryptography-dev] ECDSA Interoperablity with Microsoft CNG-based peer

André Caron andre.l.caron at gmail.com
Thu Aug 11 13:38:42 EDT 2016


Hi all,

I'm dealing with a C++ client & server pair that uses ECDSA to verify the
server's identity.  I'm trying to write a new Python client that will
exchange with the server without making any changes to the server.  I've
gotten quite a bit of this in place with cryptography (the Python package
:-), but I'm incapable of getting the Python client to verify the signature
sent by the server and I'd like to see if you can help me out.

One of the problems here is that the serialization formats seem to be
internal to Microsoft's CNG API.  For example, the public key is the raw
output of BCryptExportKey() and the signature is the raw output of
BCryptSignHash().  These are Microsoft APIs, so... needless to say
cryptography doesn't "just work" with these formats.

I'm pretty sure I managed to nail the key format conversion as Microsoft
makes an obscure reference to the format[1], but I'm still having trouble
with signatures.  The blob I get as output from BCryptSignHash() has 64
bytes, but signatures for the same algorithm using cryptography are usually
70-72 bytes, so I'm confused.  Cryptography's ECC signature computation
clearly documents the format: "The signature is formatted as DER-encoded
bytes, as specified in RFC 3279."  However, Microsoft doesn't seem to
output record an equivalent anywhere.  They're usually pretty consistent
with their APIs and storage formats, so I assume some sort of storage
similar to the keys where we have two 32-byte octet streams in big endian
format containing the values for R and S, but I haven't had any luck with
this.  I also know that the DER encoding for two integer fields will
normally add 6 bytes of overhead, which gets us up to 70, but there is
still the occasional extra 1 or 2 bytes, so I'm obviously missing something
and may not be on the right track.

[1] :https://msdn.microsoft.com/library/aa375520.aspx

Anyways, I managed to extract the BCrypt* function calls from the server
and client into a pair of C++ program the contain only the signing and
signature verification code to reproduce the flow.  The total is ~30 lines
of C++ code on each side, plus ~400 lines wrappers for BCrypt* calls
(resource management, error handling and links to CNG API docs).

I've also written a small cryptography-based Python program that tries to
mimic the C++ client and I cannot get that part to run.

If anyone has a few minutes to spare to give my Python code a second pair
of eyeballs, I'd really appreciate it.

I've saved up all of that on this Gist:
https://gist.github.com/AndreLouisCaron/ab5ee411d0722a0981feceddbf5cb3d9

The gist contents are as follows:
- genkeys.py: generate a public/private key pair, write to disk in
Microsoft's format;
- server.cpp: load secret key, compute signature, save payload & signature
to disk;
- client.cpp: load public key, payload & signature from disk, verify
signature;
- common.h: stuff shared by client.cpp & server.cpp;
- client.py: same as client.cpp, but using cryptography.

I also have an alternate C++ client based on OpenSSL which might be a
better source of inspiration.  I'll see if I can extract pars of that too
as a reference since it might be easier to map to cryptography's internals.

Thanks in advance,

André
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/cryptography-dev/attachments/20160811/78c6753f/attachment.html>


More information about the Cryptography-dev mailing list