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

André Caron andre.l.caron at gmail.com
Tue Aug 16 11:18:02 EDT 2016


Hi Alex,

Thanks a bunch for the tip!

I managed to get the signature verification step to work. Here is my code:

    signature = ...  # 64 bytes in P1363 format.

    # Load signature from Microsoft's raw format to the DER-encoded
    # format expected by cryptography.
    #
    # Conversion steps:
    # - decode public numbers as 2 big-endian octet stream;
    # - DER-encode public numbers.
    def _bin2int(backend, x):
        backend = backend._backends[0]
        i = backend._lib.BN_bin2bn(x, len(x), backend._ffi.NULL)
        return backend._bn_to_int(i)
    signature = encode_dss_signature(
        _bin2int(backend, signature[:32]),
        _bin2int(backend, signature[32:]),
    )

I also managed to get the CGN API-based signature verification step to work
when using a Cryptography-based server to sign the payload using this piece
of code:

    signature = ...  # ASN.1

    # Convert signature from the DER-encoded format used by
    #  OpenSSL into Microsoft's raw format
    #
    # Conversion steps:
    # - DER-decode numbers;
    # - encode numbers as 2 big-endian octet streams.
    def int2bin(backend, i):
        backend = backend._backends[0]
        i = backend._int_to_bn(i)
        x = backend._ffi.new(
            "unsigned char[]", backend._lib.BN_num_bytes(i)
        )
        n = backend._lib.BN_bn2bin(i, x)
        i = backend._ffi.buffer(x)[:n]
        return i
    r, s = decode_dss_signature(signature)
    signature = int2bin(backend, r) + int2bin(backend, s)

(I updated my gist with the C++ client & server and the Python client &
server if you want to look at the rest.)

I also have related snippets that allow me to convert public & private keys
to and from Microsoft's format.

Now, i have full interop: I can generate a keypair, share the public key,
sign and verify using both Python and C++ (both directions).

However, I'm relying on cryptography internals to do this, which is
definitely not desirable in the medium-long term.  Know of a better way to
do these conversions by relying only on public APIs?

Also, I guess I'm not the only person that's going to be running into
this.  Any interest in adding built-in support for this in cryptography?
If so, I'd be willing to put some effort into a PR.

Thanks,

André


On Fri, Aug 12, 2016 at 7:41 AM, Alex Gaynor <alex.gaynor at gmail.com> wrote:

> https://stackoverflow.com/questions/20992760/understanding-bcryptsignhash-
> output-signature matches your intuition: the format out of Microsoft's
> function is just the two numbers concatenated together, perhaps they are
> little endian instead of big endian though?
>
> Alex
>
> On Thu, Aug 11, 2016 at 1:38 PM, André Caron <andre.l.caron at gmail.com>
> wrote:
>
>> 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é
>>
>> _______________________________________________
>> Cryptography-dev mailing list
>> Cryptography-dev at python.org
>> https://mail.python.org/mailman/listinfo/cryptography-dev
>>
>>
>
>
> --
> "I disapprove of what you say, but I will defend to the death your right
> to say it." -- Evelyn Beatrice Hall (summarizing Voltaire)
> "The people's good is the highest law." -- Cicero
> GPG Key fingerprint: D1B3 ADC0 E023 8CA6
>
>
> _______________________________________________
> Cryptography-dev mailing list
> Cryptography-dev at python.org
> https://mail.python.org/mailman/listinfo/cryptography-dev
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/cryptography-dev/attachments/20160816/0d632512/attachment.html>


More information about the Cryptography-dev mailing list