@@ -536,6 +536,196 @@ ossl_rsa_private_decrypt(int argc, VALUE *argv, VALUE self)
536536 return str ;
537537}
538538
539+ /*
540+ * call-seq:
541+ * rsa.sign_pss(digest, data, salt_length:, mgf1_hash:) -> String
542+ *
543+ * Signs _data_ using the Probabilistic Signature Scheme (RSA-PSS) and returns
544+ * the calculated signature.
545+ *
546+ * RSAError will be raised if an error occurs.
547+ *
548+ * See #verify_pss for the verification operation.
549+ *
550+ * === Parameters
551+ * _digest_::
552+ * A String containing the message digest algorithm name.
553+ * _data_::
554+ * A String. The data to be signed.
555+ * _salt_length_::
556+ * The length in octets of the salt. Two special values are reserved:
557+ * +:digest+ means the digest length, and +:max+ means the maximum possible
558+ * length for the combination of the private key and the selected message
559+ * digest algorithm.
560+ * _mgf1_hash_::
561+ * The hash algorithm used in MGF1 (the currently supported mask generation
562+ * function (MGF)).
563+ *
564+ * === Example
565+ * data = "Sign me!"
566+ * pkey = OpenSSL::PKey::RSA.new(2048)
567+ * signature = pkey.sign_pss("SHA256", data, salt_length: :max, mgf1_hash: "SHA256")
568+ * pub_key = pkey.public_key
569+ * puts pub_key.verify_pss("SHA256", signature, data,
570+ * salt_length: :auto, mgf1_hash: "SHA256") # => true
571+ */
572+ static VALUE
573+ ossl_rsa_sign_pss (int argc , VALUE * argv , VALUE self )
574+ {
575+ VALUE digest , data , options , kwargs [2 ], signature ;
576+ static ID kwargs_ids [2 ];
577+ EVP_PKEY * pkey ;
578+ EVP_PKEY_CTX * pkey_ctx ;
579+ const EVP_MD * md , * mgf1md ;
580+ EVP_MD_CTX * md_ctx ;
581+ size_t buf_len ;
582+ int salt_len ;
583+
584+ if (!kwargs_ids [0 ]) {
585+ kwargs_ids [0 ] = rb_intern_const ("salt_length" );
586+ kwargs_ids [1 ] = rb_intern_const ("mgf1_hash" );
587+ }
588+ rb_scan_args (argc , argv , "2:" , & digest , & data , & options );
589+ rb_get_kwargs (options , kwargs_ids , 2 , 0 , kwargs );
590+ if (kwargs [0 ] == ID2SYM (rb_intern ("max" )))
591+ salt_len = -2 ; /* RSA_PSS_SALTLEN_MAX_SIGN */
592+ else if (kwargs [0 ] == ID2SYM (rb_intern ("digest" )))
593+ salt_len = -1 ; /* RSA_PSS_SALTLEN_DIGEST */
594+ else
595+ salt_len = NUM2INT (kwargs [0 ]);
596+ mgf1md = ossl_evp_get_digestbyname (kwargs [1 ]);
597+
598+ pkey = GetPrivPKeyPtr (self );
599+ buf_len = EVP_PKEY_size (pkey );
600+ md = ossl_evp_get_digestbyname (digest );
601+ StringValue (data );
602+ signature = rb_str_new (NULL , (long )buf_len );
603+
604+ md_ctx = EVP_MD_CTX_new ();
605+ if (!md_ctx )
606+ goto err ;
607+
608+ if (EVP_DigestSignInit (md_ctx , & pkey_ctx , md , NULL , pkey ) != 1 )
609+ goto err ;
610+
611+ if (EVP_PKEY_CTX_set_rsa_padding (pkey_ctx , RSA_PKCS1_PSS_PADDING ) != 1 )
612+ goto err ;
613+
614+ if (EVP_PKEY_CTX_set_rsa_pss_saltlen (pkey_ctx , salt_len ) != 1 )
615+ goto err ;
616+
617+ if (EVP_PKEY_CTX_set_rsa_mgf1_md (pkey_ctx , mgf1md ) != 1 )
618+ goto err ;
619+
620+ if (EVP_DigestSignUpdate (md_ctx , RSTRING_PTR (data ), RSTRING_LEN (data )) != 1 )
621+ goto err ;
622+
623+ if (EVP_DigestSignFinal (md_ctx , (unsigned char * )RSTRING_PTR (signature ), & buf_len ) != 1 )
624+ goto err ;
625+
626+ rb_str_set_len (signature , (long )buf_len );
627+
628+ EVP_MD_CTX_free (md_ctx );
629+ return signature ;
630+
631+ err :
632+ EVP_MD_CTX_free (md_ctx );
633+ ossl_raise (eRSAError , NULL );
634+ }
635+
636+ /*
637+ * call-seq:
638+ * rsa.verify_pss(digest, signature, data, salt_length:, mgf1_hash:) -> true | false
639+ *
640+ * Verifies _data_ using the Probabilistic Signature Scheme (RSA-PSS).
641+ *
642+ * The return value is +true+ if the signature is valid, +false+ otherwise.
643+ * RSAError will be raised if an error occurs.
644+ *
645+ * See #sign_pss for the signing operation and an example code.
646+ *
647+ * === Parameters
648+ * _digest_::
649+ * A String containing the message digest algorithm name.
650+ * _data_::
651+ * A String. The data to be signed.
652+ * _salt_length_::
653+ * The length in octets of the salt. Two special values are reserved:
654+ * +:digest+ means the digest length, and +:auto+ means automatically
655+ * determining the length based on the signature.
656+ * _mgf1_hash_::
657+ * The hash algorithm used in MGF1.
658+ */
659+ static VALUE
660+ ossl_rsa_verify_pss (int argc , VALUE * argv , VALUE self )
661+ {
662+ VALUE digest , signature , data , options , kwargs [2 ];
663+ static ID kwargs_ids [2 ];
664+ EVP_PKEY * pkey ;
665+ EVP_PKEY_CTX * pkey_ctx ;
666+ const EVP_MD * md , * mgf1md ;
667+ EVP_MD_CTX * md_ctx ;
668+ int result , salt_len ;
669+
670+ if (!kwargs_ids [0 ]) {
671+ kwargs_ids [0 ] = rb_intern_const ("salt_length" );
672+ kwargs_ids [1 ] = rb_intern_const ("mgf1_hash" );
673+ }
674+ rb_scan_args (argc , argv , "3:" , & digest , & signature , & data , & options );
675+ rb_get_kwargs (options , kwargs_ids , 2 , 0 , kwargs );
676+ if (kwargs [0 ] == ID2SYM (rb_intern ("auto" )))
677+ salt_len = -2 ; /* RSA_PSS_SALTLEN_AUTO */
678+ else if (kwargs [0 ] == ID2SYM (rb_intern ("digest" )))
679+ salt_len = -1 ; /* RSA_PSS_SALTLEN_DIGEST */
680+ else
681+ salt_len = NUM2INT (kwargs [0 ]);
682+ mgf1md = ossl_evp_get_digestbyname (kwargs [1 ]);
683+
684+ GetPKey (self , pkey );
685+ md = ossl_evp_get_digestbyname (digest );
686+ StringValue (signature );
687+ StringValue (data );
688+
689+ md_ctx = EVP_MD_CTX_new ();
690+ if (!md_ctx )
691+ goto err ;
692+
693+ if (EVP_DigestVerifyInit (md_ctx , & pkey_ctx , md , NULL , pkey ) != 1 )
694+ goto err ;
695+
696+ if (EVP_PKEY_CTX_set_rsa_padding (pkey_ctx , RSA_PKCS1_PSS_PADDING ) != 1 )
697+ goto err ;
698+
699+ if (EVP_PKEY_CTX_set_rsa_pss_saltlen (pkey_ctx , salt_len ) != 1 )
700+ goto err ;
701+
702+ if (EVP_PKEY_CTX_set_rsa_mgf1_md (pkey_ctx , mgf1md ) != 1 )
703+ goto err ;
704+
705+ if (EVP_DigestVerifyUpdate (md_ctx , RSTRING_PTR (data ), RSTRING_LEN (data )) != 1 )
706+ goto err ;
707+
708+ result = EVP_DigestVerifyFinal (md_ctx ,
709+ (unsigned char * )RSTRING_PTR (signature ),
710+ RSTRING_LEN (signature ));
711+
712+ switch (result ) {
713+ case 0 :
714+ ossl_clear_error ();
715+ EVP_MD_CTX_free (md_ctx );
716+ return Qfalse ;
717+ case 1 :
718+ EVP_MD_CTX_free (md_ctx );
719+ return Qtrue ;
720+ default :
721+ goto err ;
722+ }
723+
724+ err :
725+ EVP_MD_CTX_free (md_ctx );
726+ ossl_raise (eRSAError , NULL );
727+ }
728+
539729/*
540730 * call-seq:
541731 * rsa.params => hash
@@ -731,6 +921,8 @@ Init_ossl_rsa(void)
731921 rb_define_method (cRSA , "public_decrypt" , ossl_rsa_public_decrypt , -1 );
732922 rb_define_method (cRSA , "private_encrypt" , ossl_rsa_private_encrypt , -1 );
733923 rb_define_method (cRSA , "private_decrypt" , ossl_rsa_private_decrypt , -1 );
924+ rb_define_method (cRSA , "sign_pss" , ossl_rsa_sign_pss , -1 );
925+ rb_define_method (cRSA , "verify_pss" , ossl_rsa_verify_pss , -1 );
734926
735927 DEF_OSSL_PKEY_BN (cRSA , rsa , n );
736928 DEF_OSSL_PKEY_BN (cRSA , rsa , e );
0 commit comments