Another Ruby/OpenSSL Patch

J

Jamis Buck

--------------030406090703080608070607
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

I think I've nailed down all the issues that were preventing Net::SSH
from being fully SSH2 compliant. I had to add two new methods to
OpenSSL::pKey::DSA, to allow processing of raw sigs (since that's what
the SSH server is sending, and expecting).

I've attached a patch. With the addition of the DSA#do_verify method
that this patch exposes, Net::SSH can now successfully handle ssh-dss
server keys.

Please let me know if you need anything further to apply this patch
before the freeze on the 7th.

--
Jamis Buck
(e-mail address removed)
http://www.jamisbuck.org/jamis

ruby -ropenssl
-e'k="01234567";p((c,c.padding,c.iv,c.key=OpenSSL::Cipher::BF.new,0,k,k*2)[0].decrypt.update("1A81803C452C324619D319F980D5B84DBB45FC0FE2BAA045".scan(/../).map{|n|n.to_i(16).chr}.join))'

--------------030406090703080608070607
Content-Type: text/x-patch;
name="dsa.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="dsa.patch"

diff --exclude='*.o' --exclude='*.so' --exclude=dep --exclude=mkmf.log --exclude='*akefile*' -ur ruby.orig/ext/openssl/ossl_pkey_dsa.c ruby/ext/openssl/ossl_pkey_dsa.c
--- ruby.orig/ext/openssl/ossl_pkey_dsa.c 2004-07-01 11:24:44.388960264 -0600
+++ ruby/ext/openssl/ossl_pkey_dsa.c 2004-07-01 12:03:10.877320736 -0600
@@ -372,6 +372,109 @@
return Qfalse;
}

+/* The code for ossl_dsa_do_sign and ossl_dsa_do_verify borrows heavily from
+ * the ssh_dss_sign and ssh_dss_verify functions in the ssh-dss.c file that is
+ * part of OpenSSH distribution. The original code bears the following copyright,
+ * terms, and disclaimer:
+ *
+ * Copyright (c) 2000 Markus Friedl. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define INTBLOB_LEN 20
+#define SIGBLOB_LEN (2*INTBLOB_LEN)
+
+static VALUE
+ossl_dsa_do_sign(VALUE self, VALUE data)
+{
+ DSA_SIG *sig;
+ EVP_MD_CTX md;
+ u_char digest[EVP_MAX_MD_SIZE];
+ u_int dlen, rlen, slen;
+ EVP_PKEY *pkey;
+ VALUE signature;
+
+ GetPKeyDSA(self, pkey);
+ StringValue(data);
+
+ EVP_DigestInit(&md, EVP_sha1());
+ EVP_DigestUpdate(&md, RSTRING(data)->ptr, RSTRING(data)->len);
+ EVP_DigestFinal(&md, digest, &dlen);
+
+ sig = DSA_do_sign(digest, dlen, pkey->pkey.dsa);
+ memset(digest, 0, sizeof(digest));
+
+ rlen = BN_num_bytes(sig->r);
+ slen = BN_num_bytes(sig->s);
+ if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN)
+ {
+ DSA_SIG_free(sig);
+ rb_raise(eDSAError, "bad sig size %u %u", rlen, slen);
+ }
+
+ signature = rb_str_new(0, SIGBLOB_LEN);
+ BN_bn2bin(sig->r, RSTRING(signature)->ptr+SIGBLOB_LEN-INTBLOB_LEN-rlen);
+ BN_bn2bin(sig->s, RSTRING(signature)->ptr+SIGBLOB_LEN-slen);
+ DSA_SIG_free(sig);
+
+ return signature;
+}
+
+static VALUE
+ossl_dsa_do_verify(VALUE self, VALUE signature, VALUE data)
+{
+ DSA_SIG *sig;
+ EVP_MD_CTX md;
+ u_char digest[EVP_MAX_MD_SIZE];
+ u_int dlen;
+ int ret;
+ EVP_PKEY *pkey;
+
+ GetPKeyDSA(self, pkey);
+ StringValue(signature);
+ StringValue(data);
+
+ if (RSTRING(signature)->len != SIGBLOB_LEN)
+ rb_raise(rb_eArgError, "signature must be exactly %d bytes long", SIGBLOB_LEN );
+
+ sig = DSA_SIG_new();
+ sig->r = BN_new();
+ sig->s = BN_new();
+
+ BN_bin2bn(RSTRING(signature)->ptr, INTBLOB_LEN, sig->r);
+ BN_bin2bn(RSTRING(signature)->ptr+INTBLOB_LEN, INTBLOB_LEN, sig->s);
+
+ EVP_DigestInit(&md, EVP_sha1());
+ EVP_DigestUpdate(&md, RSTRING(data)->ptr, RSTRING(data)->len);
+ EVP_DigestFinal(&md, digest, &dlen);
+
+ ret = DSA_do_verify(digest, dlen, sig, pkey->pkey.dsa);
+ memset(digest, 0, sizeof(digest));
+
+ DSA_SIG_free(sig);
+
+ return (ret ? Qtrue : Qfalse);
+}
+
OSSL_PKEY_BN(dsa, p);
OSSL_PKEY_BN(dsa, q);
OSSL_PKEY_BN(dsa, g);
@@ -401,6 +504,8 @@
rb_define_method(cDSA, "public_key", ossl_dsa_to_public_key, 0);
rb_define_method(cDSA, "syssign", ossl_dsa_sign, 1);
rb_define_method(cDSA, "sysverify", ossl_dsa_verify, 2);
+ rb_define_method(cDSA, "do_sign", ossl_dsa_do_sign, 1);
+ rb_define_method(cDSA, "do_verify", ossl_dsa_do_verify, 2);

DEF_OSSL_PKEY_BN(cDSA, dsa, p);
DEF_OSSL_PKEY_BN(cDSA, dsa, q);

--------------030406090703080608070607--
 
G

GOTOU Yuuzou

In message said:
I've attached a patch. With the addition of the DSA#do_verify method
that this patch exposes, Net::SSH can now successfully handle ssh-dss
server keys.

I think these methods can be written with existing library.
Could you try the attached code?
--
gotoyuzo

require "openssl"

class OpenSSL::pKey::DSA
def do_sign(data)
sig = sign(OpenSSL::Digest::DSS1.new, data)
a1sig = OpenSSL::ASN1.decode(sig)
sig_r = sprintf("%.40x", a1sig.value[0].value)
sig_s = sprintf("%.40x", a1sig.value[1].value)
if sig_r.length > 40 || sig_s.length > 40
raise OpenSSL::pKey::DSAError, "bad sig size"
end
return [sig_r].pack("H*") + [sig_s].pack("H*")
end

def do_verify(sig, data)
sig_r = sig[0,20].unpack("H*")[0].to_i(16)
sig_s = sig[20,20].unpack("H*")[0].to_i(16)
a1sig = OpenSSL::ASN1::Sequence([
OpenSSL::ASN1::Integer(sig_r),
OpenSSL::ASN1::Integer(sig_s)
])
return verify(OpenSSL::Digest::DSS1.new, a1sig.to_der, data)
end
end

dsa = OpenSSL::pKey::DSA.new(512)
data = File.read(__FILE__)
sig = dsa.do_sign(data)
p dsa.do_verify(sig, data)
 
J

Jamis Buck

GOTOU said:
In message <[email protected]>,



I think these methods can be written with existing library.
Could you try the attached code?

Thank-you! That works wonderfully. Now... could I bother you for an
explanation of what the code does? Specifically, the verification code:

sig_r = sig[0,20].unpack("H*")[0].to_i(16)
sig_s = sig[20,20].unpack("H*")[0].to_i(16)
a1sig = OpenSSL::ASN1::Sequence([
OpenSSL::ASN1::Integer(sig_r),
OpenSSL::ASN1::Integer(sig_s)
])
return verify(OpenSSL::Digest::DSS1.new, a1sig.to_der, data)

I understand the first two lines, but what is an ASN1::Sequence, and
what does converting it to a "der" (on the last line) accomplish?

Thanks again!

--
Jamis Buck
(e-mail address removed)
http://www.jamisbuck.org/jamis

ruby -ropenssl
-e'k="01234567";p((c,c.padding,c.iv,c.key=OpenSSL::Cipher::BF.new,0,k,k*2)[0].decrypt.update("1A81803C452C324619D319F980D5B84DBB45FC0FE2BAA045".scan(/../).map{|n|n.to_i(16).chr}.join))'
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,054
Latest member
LucyCarper

Latest Threads

Top