openssl request - don't hardcode PKCS1 padding (and some docs)

S

Sam Roberts

I want to do unpadded RSA operations with openssl, but I discovered that
ossl_pkey_rsa.c hard-codes the use of PKCS#1 padding into the ruby
methods private/public_encrypt/decrypt.

What I think they should do is the same thing that OpenSSL does - accept
the padding type as an argument (and default to RSA_PKCS1_PADDING, as it
does now).

I don't know how to do this with ruby extensions, so I just hacked in
two functions that don't do the padding, just to see if it works (it
does), see the diff below.

Can unpadded RSA operations be added to the library? Thanks.

Btw, the openssl extension is systematically undocumented, I've begun
some docs locally (because my memory isn't good enough to use libraries
without docs). I'm happy to provide them as diffs to the .c source,
would anybody be interested?

Thanks,
Sam


++++++++++ beginning of OpenSSL docs ++++++++++++++++++++++++++
OpenSSL::pKey::RSA

new(bitsize, exp) -

Generate an RSA key pair, of +bitsize+, +exp+ is the public exponent, what's the default, 2^16 + 1?

These methods return the RSA key parameters:

Public:
#n = p * q, the public modulus
#e the public exponent, usually 2^16 + 1, but 3 is sometimes used for speed,
particularly when generating benchmarks that exagerate the speed of the
library.

Private:
#d = e ^ -1 mod (p-1)(q-1), the private exponent
#p prime component of d, used to speed up private key operations
#q prime component of d, used to speed up private key operations
#dmq1 = d mod (q-1), used to speed up private key operations
#dmp1 = d mod (p-1), used to speed up private key operations
#iqmp = q ^ -1 mod p, used to speed up private key operations


??
#d=
#dmp1=
#dmq1=
#e=
#iqmp=
#n=
#p=
#q=


Raw RSA operations:

Does m ^ d mod n. There are two modes, raw, and PKCS#1 padded. In raw
mode, m must be (less than or) equal in size to the public modulus, n.
In padded mode, m must be smaller than n - 11 bytes (the padding is
a least 11 bytes long) and it is padded out to size of n before raw
exponentiation is done.

FIXME - Currently, PKCS#1 padding is hard-coded.

#private_encrypt

Behaviour probably depends on padding mode. If there is no padding,
private_encrypt and private_decrypt should be the same. If there is
padding, then decrypt will strip the padding after the private
exponentiation (the inverse of the padded encrypt operation).

FIXME - Currently, PKCS#1 padding is hard-coded.

#private_decrypt


m ^ e mod n
#public_encrypt
#public_decrypt


Does it return true for private keys?

#public?

true is this is a private key.

#private?


PKCS#1v1.5 sign/verify?
#sign
#verify


??
params

??
to_pem
to_text
to_der

?? PEM
public_key

??
export

ruby -r openssl -e 'puts OpenSSL::pKey::RSA.new(512).methods-Object.methods'


++++ Diff to add unpadded rsa operations to openssl +++++++++++++

? openssl.bundle
Index: ossl_pkey_rsa.c
===================================================================
RCS file: /src/ruby/ext/openssl/ossl_pkey_rsa.c,v
retrieving revision 1.5.2.2
diff -u -r1.5.2.2 ossl_pkey_rsa.c
--- ossl_pkey_rsa.c 30 Jun 2004 18:34:59 -0000 1.5.2.2
+++ ossl_pkey_rsa.c 4 Dec 2004 18:43:56 -0000
@@ -272,6 +272,49 @@
}

static VALUE
+ossl_rsa_public_exp(VALUE self, VALUE buffer)
+{
+ EVP_PKEY *pkey;
+ int buf_len;
+ VALUE str;
+
+ GetPKeyRSA(self, pkey);
+ StringValue(buffer);
+ str = rb_str_new(0, ossl_rsa_buf_size(pkey));
+ buf_len = RSA_public_decrypt(RSTRING(buffer)->len, RSTRING(buffer)->ptr,
+ RSTRING(str)->ptr, pkey->pkey.rsa,
+ RSA_NO_PADDING);
+ if(buf_len < 0) ossl_raise(eRSAError, NULL);
+ RSTRING(str)->len = buf_len;
+ RSTRING(str)->ptr[buf_len] = 0;
+
+ return str;
+}
+
+static VALUE
+ossl_rsa_private_exp(VALUE self, VALUE buffer)
+{
+ EVP_PKEY *pkey;
+ int buf_len;
+ VALUE str;
+
+ GetPKeyRSA(self, pkey);
+ if (!RSA_PRIVATE(pkey->pkey.rsa)) {
+ ossl_raise(eRSAError, "PRIVATE key needed for this operation!");
+ }
+ StringValue(buffer);
+ str = rb_str_new(0, ossl_rsa_buf_size(pkey));
+ buf_len = RSA_private_encrypt(RSTRING(buffer)->len, RSTRING(buffer)->ptr,
+ RSTRING(str)->ptr, pkey->pkey.rsa,
+ RSA_NO_PADDING);
+ if (buf_len < 0) ossl_raise(eRSAError, NULL);
+ RSTRING(str)->len = buf_len;
+ RSTRING(str)->ptr[buf_len] = 0;
+
+ return str;
+}
+
+static VALUE
ossl_rsa_public_decrypt(VALUE self, VALUE buffer)
{
EVP_PKEY *pkey;
@@ -473,6 +516,9 @@
rb_define_method(cRSA, "public_decrypt", ossl_rsa_public_decrypt, 1);
rb_define_method(cRSA, "private_encrypt", ossl_rsa_private_encrypt, 1);
rb_define_method(cRSA, "private_decrypt", ossl_rsa_private_decrypt, 1);
+
+ rb_define_method(cRSA, "private_exp", ossl_rsa_private_exp, 1);
+ rb_define_method(cRSA, "public_exp", ossl_rsa_public_exp, 1);

DEF_OSSL_PKEY_BN(cRSA, rsa, n);
DEF_OSSL_PKEY_BN(cRSA, rsa, e);
 
S

Sam Roberts

Quoteing (e-mail address removed), on Sun, Dec 05, 2004 at 04:03:08AM +0900:
I want to do unpadded RSA operations with openssl, but I discovered
that ossl_pkey_rsa.c hard-codes the use of PKCS#1 padding into the
ruby methods [private/public]_[encrypt/decrypt].

So I wrote a patch, any chance of integrating this?

I think the hack I made to openssl.rb to define the constants is wrong,
but I'm not sure how this is supposed to be done.

Below is unit test and diff.

Thanks,
Sam

+++ osslraw.rb +++
require 'test/unit'

require './ext/openssl/openssl.bundle'

class Padding < Test::Unit::TestCase
def test_padding

key = OpenSSL::pKey::RSA.new(512, 3)

# 1 == PKCS1 padding, 3 = raw

#
# No padding
#

# Need right size for raw mode
plain0 = "x" * (512/8)

cipher = key.private_encrypt(plain0, 3)

plain1 = key.public_decrypt(cipher, 3)

assert_equal(plain0, plain1, plain0)

#
# PKCS1 padding, using 1 and no arg to get it, all should be equiv
#

# Need smaller size for pkcs1 mode
plain0 = "x" * (512/8 - 11)

cipherp1 = key.private_encrypt(plain0, 1)

plain1 = key.public_decrypt(cipherp1, 1)

assert_equal(plain0, plain1, plain0)

cipherdef = key.private_encrypt(plain0)

plain1 = key.public_decrypt(cipherdef)

assert_equal(plain0, plain1, plain0)

assert_equal(cipherp1, cipherdef)

# Failure cases
assert_raise(ArgumentError) { key.private_encrypt() }
assert_raise(ArgumentError) { key.private_encrypt("hi", 1, nil) }
assert_raise(TypeError) { key.private_encrypt(plain0, nil) }
assert_raise(OpenSSL::pKey::RSAError) { key.private_encrypt(plain0, 666) }

end
end

+++ patch +++
Index: ext/openssl/ossl_pkey_rsa.c
===================================================================
RCS file: /src/ruby/ext/openssl/ossl_pkey_rsa.c,v
retrieving revision 1.5.2.2
diff -u -r1.5.2.2 ossl_pkey_rsa.c
--- ext/openssl/ossl_pkey_rsa.c 30 Jun 2004 18:34:59 -0000 1.5.2.2
+++ ext/openssl/ossl_pkey_rsa.c 4 Dec 2004 20:10:33 -0000
@@ -228,7 +228,7 @@
ossl_rsa_to_der(VALUE self)
{
EVP_PKEY *pkey;
- int (*i2d_func)_((const RSA*, unsigned char**));
+ int (*i2d_func)_((RSA*, unsigned char**));
unsigned char *p;
long len;
VALUE str;
@@ -249,21 +249,42 @@
return str;
}

+static void
+ossl_crypt_args(int argc, VALUE* values, VALUE* buffer, int* padding)
+{
+ if(argc == 0)
+ rb_raise(rb_eArgError, "wrong number of arguments (0 for 1)");
+
+ if(argc > 2)
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc);
+
+ *buffer = values[0];
+
+ /* Default to old behaviour, PKCS#1 padding. */
+ if(argc == 1)
+ *padding = RSA_PKCS1_PADDING;
+ else
+ *padding = (int) NUM2INT(values[1]);
+}
+
#define ossl_rsa_buf_size(pkey) (RSA_size((pkey)->pkey.rsa)+16)

static VALUE
-ossl_rsa_public_encrypt(VALUE self, VALUE buffer)
+ossl_rsa_public_encrypt(int argc, VALUE* values, VALUE self)
{
EVP_PKEY *pkey;
int buf_len;
VALUE str;
+ VALUE buffer;
+ int padding;
+ ossl_crypt_args(argc, values, &buffer, &padding);

GetPKeyRSA(self, pkey);
StringValue(buffer);
str = rb_str_new(0, ossl_rsa_buf_size(pkey));
buf_len = RSA_public_encrypt(RSTRING(buffer)->len, RSTRING(buffer)->ptr,
RSTRING(str)->ptr, pkey->pkey.rsa,
- RSA_PKCS1_PADDING);
+ padding);
if (buf_len < 0) ossl_raise(eRSAError, NULL);
RSTRING(str)->len = buf_len;
RSTRING(str)->ptr[buf_len] = 0;
@@ -272,18 +293,21 @@
}

static VALUE
-ossl_rsa_public_decrypt(VALUE self, VALUE buffer)
+ossl_rsa_public_decrypt(int argc, VALUE* values, VALUE self)
{
EVP_PKEY *pkey;
int buf_len;
VALUE str;
+ VALUE buffer;
+ int padding;
+ ossl_crypt_args(argc, values, &buffer, &padding);

GetPKeyRSA(self, pkey);
StringValue(buffer);
str = rb_str_new(0, ossl_rsa_buf_size(pkey));
buf_len = RSA_public_decrypt(RSTRING(buffer)->len, RSTRING(buffer)->ptr,
RSTRING(str)->ptr, pkey->pkey.rsa,
- RSA_PKCS1_PADDING);
+ padding);
if(buf_len < 0) ossl_raise(eRSAError, NULL);
RSTRING(str)->len = buf_len;
RSTRING(str)->ptr[buf_len] = 0;
@@ -292,11 +316,14 @@
}

static VALUE
-ossl_rsa_private_encrypt(VALUE self, VALUE buffer)
+ossl_rsa_private_encrypt(int argc, VALUE* values, VALUE self)
{
EVP_PKEY *pkey;
int buf_len;
VALUE str;
+ VALUE buffer;
+ int padding;
+ ossl_crypt_args(argc, values, &buffer, &padding);

GetPKeyRSA(self, pkey);
if (!RSA_PRIVATE(pkey->pkey.rsa)) {
@@ -306,7 +333,7 @@
str = rb_str_new(0, ossl_rsa_buf_size(pkey));
buf_len = RSA_private_encrypt(RSTRING(buffer)->len, RSTRING(buffer)->ptr,
RSTRING(str)->ptr, pkey->pkey.rsa,
- RSA_PKCS1_PADDING);
+ padding);
if (buf_len < 0) ossl_raise(eRSAError, NULL);
RSTRING(str)->len = buf_len;
RSTRING(str)->ptr[buf_len] = 0;
@@ -315,11 +342,14 @@
}

static VALUE
-ossl_rsa_private_decrypt(VALUE self, VALUE buffer)
+ossl_rsa_private_decrypt(int argc, VALUE* values, VALUE self)
{
EVP_PKEY *pkey;
int buf_len;
VALUE str;
+ VALUE buffer;
+ int padding;
+ ossl_crypt_args(argc, values, &buffer, &padding);

GetPKeyRSA(self, pkey);
if (!RSA_PRIVATE(pkey->pkey.rsa)) {
@@ -329,7 +359,7 @@
str = rb_str_new(0, ossl_rsa_buf_size(pkey));
buf_len = RSA_private_decrypt(RSTRING(buffer)->len, RSTRING(buffer)->ptr,
RSTRING(str)->ptr, pkey->pkey.rsa,
- RSA_PKCS1_PADDING);
+ padding);
if (buf_len < 0) ossl_raise(eRSAError, NULL);
RSTRING(str)->len = buf_len;
RSTRING(str)->ptr[buf_len] = 0;
@@ -469,10 +499,10 @@
rb_define_alias(cRSA, "to_s", "export");
rb_define_method(cRSA, "to_der", ossl_rsa_to_der, 0);
rb_define_method(cRSA, "public_key", ossl_rsa_to_public_key, 0);
- rb_define_method(cRSA, "public_encrypt", ossl_rsa_public_encrypt, 1);
- rb_define_method(cRSA, "public_decrypt", ossl_rsa_public_decrypt, 1);
- rb_define_method(cRSA, "private_encrypt", ossl_rsa_private_encrypt, 1);
- rb_define_method(cRSA, "private_decrypt", ossl_rsa_private_decrypt, 1);
+ rb_define_method(cRSA, "public_encrypt", ossl_rsa_public_encrypt, -1);
+ rb_define_method(cRSA, "public_decrypt", ossl_rsa_public_decrypt, -1);
+ rb_define_method(cRSA, "private_encrypt", ossl_rsa_private_encrypt, -1);
+ rb_define_method(cRSA, "private_decrypt", ossl_rsa_private_decrypt, -1);

DEF_OSSL_PKEY_BN(cRSA, rsa, n);
DEF_OSSL_PKEY_BN(cRSA, rsa, e);
Index: ext/openssl/lib/openssl.rb
===================================================================
RCS file: /src/ruby/ext/openssl/lib/openssl.rb,v
retrieving revision 1.1
diff -u -r1.1 openssl.rb
--- ext/openssl/lib/openssl.rb 23 Jul 2003 16:11:29 -0000 1.1
+++ ext/openssl/lib/openssl.rb 4 Dec 2004 20:10:33 -0000
@@ -22,3 +22,10 @@
require 'openssl/ssl'
require 'openssl/x509'

+module OpenSSL
+ RSA_PKCS1_PADDING = 1
+ RSA_SSLV23_PADDING = 2
+ RSA_NO_PADDING = 3
+ RSA_PKCS1_OAEP_PADDING = 4
+end
+
 
G

GOTOU Yuuzou

Hi,

In message said:
Quoteing (e-mail address removed), on Sun, Dec 05, 2004 at 04:03:08AM +0900:
I want to do unpadded RSA operations with openssl, but I discovered
that ossl_pkey_rsa.c hard-codes the use of PKCS#1 padding into the
ruby methods [private/public]_[encrypt/decrypt].

got it.
So I wrote a patch, any chance of integrating this?

Thanks. I'll apply your patch with a bit of arrangement.
 
S

Sam Roberts

Quoteing (e-mail address removed), on Sun, Dec 05, 2004 at 01:51:00PM +0900:
In message said:
Quoteing (e-mail address removed), on Sun, Dec 05, 2004 at 04:03:08AM +0900:
I want to do unpadded RSA operations with openssl, but I discovered
that ossl_pkey_rsa.c hard-codes the use of PKCS#1 padding into the
ruby methods [private/public]_[encrypt/decrypt].
So I wrote a patch, any chance of integrating this?

Thanks. I'll apply your patch with a bit of arrangement.

Thank you, I look forward to it showing up in cvs.

Sam
 

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,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top