diff options
Diffstat (limited to 'ext/openssl/ossl_pkey_rsa.c')
-rw-r--r-- | ext/openssl/ossl_pkey_rsa.c | 230 |
1 files changed, 211 insertions, 19 deletions
diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c index 1fcdd52c..4800fb27 100644 --- a/ext/openssl/ossl_pkey_rsa.c +++ b/ext/openssl/ossl_pkey_rsa.c @@ -172,8 +172,8 @@ rsa_generate(int size, unsigned long exp) * RSA.generate(size) => RSA instance * RSA.generate(size, exponent) => RSA instance * - * Generates an RSA keypair. +size+ is an integer representing the desired key - * size. Keys smaller than 1024 should be considered insecure. +exponent+ is + * Generates an RSA keypair. _size_ is an integer representing the desired key + * size. Keys smaller than 1024 should be considered insecure. _exponent_ is * an odd number normally 3, 17, or 65537. */ static VALUE @@ -203,12 +203,12 @@ ossl_rsa_s_generate(int argc, VALUE *argv, VALUE klass) * RSA.new(encoded_key) => RSA instance * RSA.new(encoded_key, pass_phrase) => RSA instance * - * Generates or loads an RSA keypair. If an integer +key_size+ is given it + * Generates or loads an RSA keypair. If an integer _key_size_ is given it * represents the desired key size. Keys less than 1024 bits should be * considered insecure. * - * A key can instead be loaded from an +encoded_key+ which must be PEM or DER - * encoded. A +pass_phrase+ can be used to decrypt the key. If none is given + * A key can instead be loaded from an _encoded_key_ which must be PEM or DER + * encoded. A _pass_phrase_ can be used to decrypt the key. If none is given * OpenSSL will prompt for the pass phrase. * * = Examples @@ -295,7 +295,7 @@ ossl_rsa_initialize_copy(VALUE self, VALUE other) * call-seq: * rsa.public? => true * - * The return value is always true since every private key is also a public + * The return value is always +true+ since every private key is also a public * key. */ static VALUE @@ -333,8 +333,8 @@ ossl_rsa_is_private(VALUE self) * rsa.to_pem([cipher, pass_phrase]) => PEM-format String * rsa.to_s([cipher, pass_phrase]) => PEM-format String * - * Outputs this keypair in PEM encoding. If +cipher+ and +pass_phrase+ are - * given they will be used to encrypt the key. +cipher+ must be an + * Outputs this keypair in PEM encoding. If _cipher_ and _pass_phrase_ are + * given they will be used to encrypt the key. _cipher_ must be an * OpenSSL::Cipher instance. */ static VALUE @@ -350,7 +350,7 @@ ossl_rsa_export(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "02", &cipher, &pass); if (!NIL_P(cipher)) { - ciph = GetCipherPtr(cipher); + ciph = ossl_evp_get_cipherbyname(cipher); pass = ossl_pem_passwd_value(pass); } if (!(out = BIO_new(BIO_s_mem()))) { @@ -409,7 +409,7 @@ ossl_rsa_to_der(VALUE self) * rsa.public_encrypt(string) => String * rsa.public_encrypt(string, padding) => String * - * Encrypt +string+ with the public key. +padding+ defaults to PKCS1_PADDING. + * Encrypt _string_ with the public key. _padding_ defaults to PKCS1_PADDING. * The encrypted string output can be decrypted using #private_decrypt. */ static VALUE @@ -441,8 +441,8 @@ ossl_rsa_public_encrypt(int argc, VALUE *argv, VALUE self) * rsa.public_decrypt(string) => String * rsa.public_decrypt(string, padding) => String * - * Decrypt +string+, which has been encrypted with the private key, with the - * public key. +padding+ defaults to PKCS1_PADDING. + * Decrypt _string_, which has been encrypted with the private key, with the + * public key. _padding_ defaults to PKCS1_PADDING. */ static VALUE ossl_rsa_public_decrypt(int argc, VALUE *argv, VALUE self) @@ -473,7 +473,7 @@ ossl_rsa_public_decrypt(int argc, VALUE *argv, VALUE self) * rsa.private_encrypt(string) => String * rsa.private_encrypt(string, padding) => String * - * Encrypt +string+ with the private key. +padding+ defaults to PKCS1_PADDING. + * Encrypt _string_ with the private key. _padding_ defaults to PKCS1_PADDING. * The encrypted string output can be decrypted using #public_decrypt. */ static VALUE @@ -507,8 +507,8 @@ ossl_rsa_private_encrypt(int argc, VALUE *argv, VALUE self) * rsa.private_decrypt(string) => String * rsa.private_decrypt(string, padding) => String * - * Decrypt +string+, which has been encrypted with the public key, with the - * private key. +padding+ defaults to PKCS1_PADDING. + * Decrypt _string_, which has been encrypted with the public key, with the + * private key. _padding_ defaults to PKCS1_PADDING. */ static VALUE ossl_rsa_private_decrypt(int argc, VALUE *argv, VALUE self) @@ -538,6 +538,196 @@ ossl_rsa_private_decrypt(int argc, VALUE *argv, VALUE self) /* * call-seq: + * rsa.sign_pss(digest, data, salt_length:, mgf1_hash:) -> String + * + * Signs _data_ using the Probabilistic Signature Scheme (RSA-PSS) and returns + * the calculated signature. + * + * RSAError will be raised if an error occurs. + * + * See #verify_pss for the verification operation. + * + * === Parameters + * _digest_:: + * A String containing the message digest algorithm name. + * _data_:: + * A String. The data to be signed. + * _salt_length_:: + * The length in octets of the salt. Two special values are reserved: + * +:digest+ means the digest length, and +:max+ means the maximum possible + * length for the combination of the private key and the selected message + * digest algorithm. + * _mgf1_hash_:: + * The hash algorithm used in MGF1 (the currently supported mask generation + * function (MGF)). + * + * === Example + * data = "Sign me!" + * pkey = OpenSSL::PKey::RSA.new(2048) + * signature = pkey.sign_pss("SHA256", data, salt_length: :max, mgf1_hash: "SHA256") + * pub_key = pkey.public_key + * puts pub_key.verify_pss("SHA256", signature, data, + * salt_length: :auto, mgf1_hash: "SHA256") # => true + */ +static VALUE +ossl_rsa_sign_pss(int argc, VALUE *argv, VALUE self) +{ + VALUE digest, data, options, kwargs[2], signature; + static ID kwargs_ids[2]; + EVP_PKEY *pkey; + EVP_PKEY_CTX *pkey_ctx; + const EVP_MD *md, *mgf1md; + EVP_MD_CTX *md_ctx; + size_t buf_len; + int salt_len; + + if (!kwargs_ids[0]) { + kwargs_ids[0] = rb_intern_const("salt_length"); + kwargs_ids[1] = rb_intern_const("mgf1_hash"); + } + rb_scan_args(argc, argv, "2:", &digest, &data, &options); + rb_get_kwargs(options, kwargs_ids, 2, 0, kwargs); + if (kwargs[0] == ID2SYM(rb_intern("max"))) + salt_len = -2; /* RSA_PSS_SALTLEN_MAX_SIGN */ + else if (kwargs[0] == ID2SYM(rb_intern("digest"))) + salt_len = -1; /* RSA_PSS_SALTLEN_DIGEST */ + else + salt_len = NUM2INT(kwargs[0]); + mgf1md = ossl_evp_get_digestbyname(kwargs[1]); + + pkey = GetPrivPKeyPtr(self); + buf_len = EVP_PKEY_size(pkey); + md = ossl_evp_get_digestbyname(digest); + StringValue(data); + signature = rb_str_new(NULL, (long)buf_len); + + md_ctx = EVP_MD_CTX_new(); + if (!md_ctx) + goto err; + + if (EVP_DigestSignInit(md_ctx, &pkey_ctx, md, NULL, pkey) != 1) + goto err; + + if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) + goto err; + + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, salt_len) != 1) + goto err; + + if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1md) != 1) + goto err; + + if (EVP_DigestSignUpdate(md_ctx, RSTRING_PTR(data), RSTRING_LEN(data)) != 1) + goto err; + + if (EVP_DigestSignFinal(md_ctx, (unsigned char *)RSTRING_PTR(signature), &buf_len) != 1) + goto err; + + rb_str_set_len(signature, (long)buf_len); + + EVP_MD_CTX_free(md_ctx); + return signature; + + err: + EVP_MD_CTX_free(md_ctx); + ossl_raise(eRSAError, NULL); +} + +/* + * call-seq: + * rsa.verify_pss(digest, signature, data, salt_length:, mgf1_hash:) -> true | false + * + * Verifies _data_ using the Probabilistic Signature Scheme (RSA-PSS). + * + * The return value is +true+ if the signature is valid, +false+ otherwise. + * RSAError will be raised if an error occurs. + * + * See #sign_pss for the signing operation and an example code. + * + * === Parameters + * _digest_:: + * A String containing the message digest algorithm name. + * _data_:: + * A String. The data to be signed. + * _salt_length_:: + * The length in octets of the salt. Two special values are reserved: + * +:digest+ means the digest length, and +:auto+ means automatically + * determining the length based on the signature. + * _mgf1_hash_:: + * The hash algorithm used in MGF1. + */ +static VALUE +ossl_rsa_verify_pss(int argc, VALUE *argv, VALUE self) +{ + VALUE digest, signature, data, options, kwargs[2]; + static ID kwargs_ids[2]; + EVP_PKEY *pkey; + EVP_PKEY_CTX *pkey_ctx; + const EVP_MD *md, *mgf1md; + EVP_MD_CTX *md_ctx; + int result, salt_len; + + if (!kwargs_ids[0]) { + kwargs_ids[0] = rb_intern_const("salt_length"); + kwargs_ids[1] = rb_intern_const("mgf1_hash"); + } + rb_scan_args(argc, argv, "3:", &digest, &signature, &data, &options); + rb_get_kwargs(options, kwargs_ids, 2, 0, kwargs); + if (kwargs[0] == ID2SYM(rb_intern("auto"))) + salt_len = -2; /* RSA_PSS_SALTLEN_AUTO */ + else if (kwargs[0] == ID2SYM(rb_intern("digest"))) + salt_len = -1; /* RSA_PSS_SALTLEN_DIGEST */ + else + salt_len = NUM2INT(kwargs[0]); + mgf1md = ossl_evp_get_digestbyname(kwargs[1]); + + GetPKey(self, pkey); + md = ossl_evp_get_digestbyname(digest); + StringValue(signature); + StringValue(data); + + md_ctx = EVP_MD_CTX_new(); + if (!md_ctx) + goto err; + + if (EVP_DigestVerifyInit(md_ctx, &pkey_ctx, md, NULL, pkey) != 1) + goto err; + + if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) + goto err; + + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, salt_len) != 1) + goto err; + + if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1md) != 1) + goto err; + + if (EVP_DigestVerifyUpdate(md_ctx, RSTRING_PTR(data), RSTRING_LEN(data)) != 1) + goto err; + + result = EVP_DigestVerifyFinal(md_ctx, + (unsigned char *)RSTRING_PTR(signature), + RSTRING_LEN(signature)); + + switch (result) { + case 0: + ossl_clear_error(); + EVP_MD_CTX_free(md_ctx); + return Qfalse; + case 1: + EVP_MD_CTX_free(md_ctx); + return Qtrue; + default: + goto err; + } + + err: + EVP_MD_CTX_free(md_ctx); + ossl_raise(eRSAError, NULL); +} + +/* + * call-seq: * rsa.params => hash * * THIS METHOD IS INSECURE, PRIVATE INFORMATION CAN LEAK OUT!!! @@ -659,7 +849,7 @@ ossl_rsa_blinding_off(VALUE self) * call-seq: * rsa.set_key(n, e, d) -> self * - * Sets +n+, +e+, +d+ for the RSA instance. + * Sets _n_, _e_, _d_ for the RSA instance. */ OSSL_PKEY_BN_DEF3(rsa, RSA, key, n, e, d) /* @@ -667,7 +857,7 @@ OSSL_PKEY_BN_DEF3(rsa, RSA, key, n, e, d) * call-seq: * rsa.set_factors(p, q) -> self * - * Sets +p+, +q+ for the RSA instance. + * Sets _p_, _q_ for the RSA instance. */ OSSL_PKEY_BN_DEF2(rsa, RSA, factors, p, q) /* @@ -675,7 +865,7 @@ OSSL_PKEY_BN_DEF2(rsa, RSA, factors, p, q) * call-seq: * rsa.set_crt_params(dmp1, dmq1, iqmp) -> self * - * Sets +dmp1+, +dmq1+, +iqmp+ for the RSA instance. They are calculated by + * Sets _dmp1_, _dmq1_, _iqmp_ for the RSA instance. They are calculated by * <tt>d mod (p - 1)</tt>, <tt>d mod (q - 1)</tt> and <tt>q^(-1) mod p</tt> * respectively. */ @@ -717,7 +907,7 @@ Init_ossl_rsa(void) rb_define_singleton_method(cRSA, "generate", ossl_rsa_s_generate, -1); rb_define_method(cRSA, "initialize", ossl_rsa_initialize, -1); - rb_define_copy_func(cRSA, ossl_rsa_initialize_copy); + rb_define_method(cRSA, "initialize_copy", ossl_rsa_initialize_copy, 1); rb_define_method(cRSA, "public?", ossl_rsa_is_public, 0); rb_define_method(cRSA, "private?", ossl_rsa_is_private, 0); @@ -731,6 +921,8 @@ Init_ossl_rsa(void) 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, "sign_pss", ossl_rsa_sign_pss, -1); + rb_define_method(cRSA, "verify_pss", ossl_rsa_verify_pss, -1); DEF_OSSL_PKEY_BN(cRSA, rsa, n); DEF_OSSL_PKEY_BN(cRSA, rsa, e); |