For posterity, my two functions can be replaced with these: from cryptography.utils import int_from_bytes, int_to_bytes from cryptography.hazmat.primitives.asymmetric.utils import ( decode_dss_signature, encode_dss_signature, )
def decode_ieee_p1363_signature(data): """Decode ECDSA signature in IEEE P1363 format to ASN.1 (DER).""" return encode_dss_signature( int_from_bytes(data[:32], 'big'), int_from_bytes(data[32:], 'big'), ) def encode_ieee_p1363_signature(data): """Encode ECDSA signature in ASN.1 (DER) to IEEE P1363 format.""" r, s = decode_dss_signature(data) return int_to_bytes(r, 32) + int_to_bytes(s, 32) André On Tue, Aug 16, 2016 at 11:18 AM, André Caron <andre.l.ca...@gmail.com> wrote: > 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.gay...@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.ca...@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@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@python.org >> https://mail.python.org/mailman/listinfo/cryptography-dev >> >> >
_______________________________________________ Cryptography-dev mailing list Cryptography-dev@python.org https://mail.python.org/mailman/listinfo/cryptography-dev