7

I am working on a web application which must be able to encrypt data with ECC on the server side and decrypt it in the browser. The only library I have found that is capable of this in JS is SJCL. However, since ECC support in SJCL seems a bit abandoned at the moment, I have used a fork, which has key serialization support and a demo for easier understanding.

First, I generate an ECC key pair in JS:

keypair = sjcl.ecc.elGamal.generateKeys(384, 10);
document.writeln(JSON.stringify(keypair.pub.serialize()));

This outputs something like:

{"point":[1110230655,241884220,775655552,-849225963,-883815628,-1984298210,-736346431,1387519594,-1810604283,-1235638489,1333314084,-1219216530,614640565,-1148742381,1038670260,1013716131,758346573,1162278003,1232401864,-1948620456,533899535,-1478577959,1853846180,-1553049184],"curve":384}

Then I have tried to convert this public key to a format understandable by OpenSSL.

ar = [1110230655,241884220,775655552,-849225963,-883815628,-1984298210,-736346431,1387519594,-1810604283,-1235638489,1333314084,-1219216530,614640565,-1148742381,1038670260,1013716131,758346573,1162278003,1232401864,-1948620456,533899535,-1478577959,1853846180,-1553049184]

# ugly bit magic to somehow convert the above array into a proper byte array (in form of a string)
kstr = [(ar.map { |i| (i>=0)?('0'*(8-i.to_s(16).length)+i.to_s(16)):("%08X" % (2**32-1+i+1)) }*'').upcase].pack("H*")

# opening a public key generated with the openssl cli tool showed a structure like this:
algokey = OpenSSL::ASN1::ObjectId 'id-ecPublicKey'
algovalue = OpenSSL::ASN1::ObjectId 'secp384r1'
algo = OpenSSL::ASN1::Sequence.new [algokey,algovalue]
# for some reason OpenSSL seems to prepend 0x04 to all public keys
key = OpenSSL::ASN1::BitString.new "\x04#{kstr}"
root = OpenSSL::ASN1::Sequence.new [algo,key]

pub = OpenSSL::PKey.read(root.to_der)

Until this point, my code works fine. That is, it does not produce any exceptions.

However, when generating a shared secret with both libraries, I found that SJCL generated a 'tag' that was 96 bytes long, while OpenSSL emitted 48 bytes.

Turns out my problem is that SJCL does not use plain ECDH. It uses something that seems to be ECMQV based on a quick google search. Therefore, the 'tag' SJCL output was a point on the curve (x and y coordinates of a point, 2*48 bytes), while what OpenSSL output was a shared secret (x coordinate of a point, as dictated by ECDH).

My problem is that I don't know if there is any support for ECMQV in OpenSSL (there are some patent problems, if I'm correct). Even if there was, the ruby binding does not seem to support it.

So my actual questions:

  • are my findings documented above correct?
  • if yes, does anyone know any other ruby library which I could use instead of OpenSSL, that supports ECMQV?
gsx1022
  • 73
  • 1
  • 4
  • Why not ask the devs of SJCL? Are you aware that most schemes using crypto in the browser (without applying SSL/TLS) are insecure? – Maarten Bodewes Jun 24 '12 at 13:23
  • Well, seems like asking the devs is pointless now. :) And about being insecure: I'm aware of it, I'm not trying to implement poor man's SSL. It's about protecting user data from intruders compromising the server. Of course an intruder could modify the JS code on the server to send the secret key passphrase to him via AJAX or something, but that's a different story. – gsx1022 Jun 24 '12 at 16:48
  • I don't know if it is a different story, it's an attack scenario on your security model. But if you keep the user data local to the browser, it may indeed add security in case e.g. the data container holding the users data gets stolen. – Maarten Bodewes Jun 24 '12 at 17:15
  • "... know any other ruby library which I could use instead of OpenSSL, that supports ECMQV?" - I believe MQV has some deficiencies and should not be used. You should use HMQV, FHMQV, or ECDH. – jww Mar 13 '14 at 13:13

1 Answers1

2

It looks like you're using ElGamal in your javascript code. I couldn't really find any implementation for ruby, alternatives are using Crypto++ or libgcrypt and writing some glue code.

Ps: instead of that kstr = line, you can simply write kstr = ar.pack 'N*'

DirtY iCE
  • 431
  • 2
  • 12
  • Well, I was never really good at maths. Thanks for pointing that out. I guess I'll have to go with a cryptopp wrapper. Fine. And once again wow. Looks like I'll have to take a look at the ruby-doc for pack. – gsx1022 Jun 24 '12 at 16:51