19
19
VALUE mPKey ;
20
20
VALUE cPKey ;
21
21
VALUE ePKeyError ;
22
- static ID id_private_q ;
22
+ ID ossl_pkey_feature_id ;
23
23
24
24
static void
25
25
ossl_evp_pkey_free (void * ptr )
@@ -65,7 +65,7 @@ pkey_new0(VALUE arg)
65
65
}
66
66
67
67
VALUE
68
- ossl_pkey_new (EVP_PKEY * pkey )
68
+ ossl_pkey_new (EVP_PKEY * pkey , enum ossl_pkey_feature ps )
69
69
{
70
70
VALUE obj ;
71
71
int status ;
@@ -75,6 +75,7 @@ ossl_pkey_new(EVP_PKEY *pkey)
75
75
EVP_PKEY_free (pkey );
76
76
rb_jump_tag (status );
77
77
}
78
+ ossl_pkey_set (obj , ps );
78
79
79
80
return obj ;
80
81
}
@@ -83,34 +84,61 @@ ossl_pkey_new(EVP_PKEY *pkey)
83
84
# include <openssl/decoder.h>
84
85
85
86
EVP_PKEY *
86
- ossl_pkey_read_generic (BIO * bio , VALUE pass , const char * input_type )
87
+ ossl_pkey_read_generic (BIO * bio , VALUE pass , const char * input_type , enum ossl_pkey_feature * ps )
87
88
{
88
89
void * ppass = (void * )pass ;
89
90
OSSL_DECODER_CTX * dctx ;
90
91
EVP_PKEY * pkey = NULL ;
91
92
int pos = 0 , pos2 ;
93
+ size_t i ;
92
94
93
95
dctx = OSSL_DECODER_CTX_new_for_pkey (& pkey , "DER" , NULL , input_type , 0 , NULL , NULL );
94
96
if (!dctx )
95
97
goto out ;
96
98
if (OSSL_DECODER_CTX_set_pem_password_cb (dctx , ossl_pem_passwd_cb , ppass ) != 1 )
97
99
goto out ;
98
100
101
+ /*
102
+ * This is inefficient as it will parse the same DER/PEM encoding
103
+ * repeatedly, but OpenSSL 3.0 doesn't provide an API to return what
104
+ * information an EVP_PKEY is holding.
105
+ * OpenSSL issue: https://github.com/openssl/openssl/issues/9467
106
+ */
107
+ struct { int selection ; enum ossl_pkey_feature ps ; } selections [] = {
108
+ { EVP_PKEY_KEYPAIR , OSSL_PKEY_HAS_PRIVATE },
109
+ { EVP_PKEY_PUBLIC_KEY , OSSL_PKEY_HAS_PUBLIC },
110
+ { EVP_PKEY_KEY_PARAMETERS , OSSL_PKEY_HAS_NONE },
111
+ };
112
+
99
113
/* First check DER */
100
- if (OSSL_DECODER_from_bio (dctx , bio ) == 1 )
101
- goto out ;
114
+ for (i = 0 ; i < sizeof (selections )/sizeof (selections [0 ]); i ++ ) {
115
+ OSSL_DECODER_CTX_set_selection (dctx , selections [i ].selection );
116
+ * ps = selections [i ].ps ;
117
+ if (OSSL_DECODER_from_bio (dctx , bio ) == 1 )
118
+ goto out ;
119
+ OSSL_BIO_reset (bio );
120
+ }
102
121
103
- /* Then check PEM; multiple OSSL_DECODER_from_bio() calls may be needed */
104
- OSSL_BIO_reset (bio );
122
+ /*
123
+ * Then check PEM; multiple OSSL_DECODER_from_bio() calls may be needed for
124
+ * each selection in case of stacked PEM structures
125
+ */
105
126
if (OSSL_DECODER_CTX_set_input_type (dctx , "PEM" ) != 1 )
106
127
goto out ;
107
- while (OSSL_DECODER_from_bio (dctx , bio ) != 1 ) {
108
- if (BIO_eof (bio ))
109
- goto out ;
110
- pos2 = BIO_tell (bio );
111
- if (pos2 < 0 || pos2 <= pos )
112
- goto out ;
113
- pos = pos2 ;
128
+ for (i = 0 ; i < sizeof (selections )/sizeof (selections [0 ]); i ++ ) {
129
+ OSSL_DECODER_CTX_set_selection (dctx , selections [i ].selection );
130
+ * ps = selections [i ].ps ;
131
+ while (true) {
132
+ if (OSSL_DECODER_from_bio (dctx , bio ) == 1 )
133
+ goto out ;
134
+ if (BIO_eof (bio ))
135
+ break ;
136
+ pos2 = BIO_tell (bio );
137
+ if (pos2 < 0 || pos2 <= pos )
138
+ break ;
139
+ pos = pos2 ;
140
+ }
141
+ OSSL_BIO_reset (bio );
114
142
}
115
143
116
144
out :
@@ -119,26 +147,35 @@ ossl_pkey_read_generic(BIO *bio, VALUE pass, const char *input_type)
119
147
}
120
148
#else
121
149
EVP_PKEY *
122
- ossl_pkey_read_generic (BIO * bio , VALUE pass , const char * input_type )
150
+ ossl_pkey_read_generic (BIO * bio , VALUE pass , const char * input_type , enum ossl_pkey_feature * ps )
123
151
{
124
152
void * ppass = (void * )pass ;
125
153
EVP_PKEY * pkey ;
126
154
155
+ * ps = OSSL_PKEY_HAS_PRIVATE ;
127
156
if ((pkey = d2i_PrivateKey_bio (bio , NULL )))
128
157
goto out ;
129
158
OSSL_BIO_reset (bio );
130
159
if ((pkey = d2i_PKCS8PrivateKey_bio (bio , NULL , ossl_pem_passwd_cb , ppass )))
131
160
goto out ;
161
+
162
+ * ps = OSSL_PKEY_HAS_PUBLIC ;
132
163
OSSL_BIO_reset (bio );
133
164
if ((pkey = d2i_PUBKEY_bio (bio , NULL )))
134
165
goto out ;
135
- OSSL_BIO_reset ( bio );
166
+
136
167
/* PEM_read_bio_PrivateKey() also parses PKCS #8 formats */
168
+ * ps = OSSL_PKEY_HAS_PRIVATE ;
169
+ OSSL_BIO_reset (bio );
137
170
if ((pkey = PEM_read_bio_PrivateKey (bio , NULL , ossl_pem_passwd_cb , ppass )))
138
171
goto out ;
172
+
173
+ * ps = OSSL_PKEY_HAS_PUBLIC ;
139
174
OSSL_BIO_reset (bio );
140
175
if ((pkey = PEM_read_bio_PUBKEY (bio , NULL , NULL , NULL )))
141
176
goto out ;
177
+
178
+ * ps = OSSL_PKEY_HAS_NONE ;
142
179
OSSL_BIO_reset (bio );
143
180
if ((pkey = PEM_read_bio_Parameters (bio , NULL )))
144
181
goto out ;
@@ -195,14 +232,15 @@ ossl_pkey_new_from_data(int argc, VALUE *argv, VALUE self)
195
232
EVP_PKEY * pkey ;
196
233
BIO * bio ;
197
234
VALUE data , pass ;
235
+ enum ossl_pkey_feature ps ;
198
236
199
237
rb_scan_args (argc , argv , "11" , & data , & pass );
200
238
bio = ossl_obj2bio (& data );
201
- pkey = ossl_pkey_read_generic (bio , ossl_pem_passwd_value (pass ), NULL );
239
+ pkey = ossl_pkey_read_generic (bio , ossl_pem_passwd_value (pass ), NULL , & ps );
202
240
BIO_free (bio );
203
241
if (!pkey )
204
242
ossl_raise (ePKeyError , "Could not parse PKey" );
205
- return ossl_pkey_new (pkey );
243
+ return ossl_pkey_new (pkey , ps );
206
244
}
207
245
208
246
static VALUE
@@ -405,7 +443,7 @@ pkey_generate(int argc, VALUE *argv, VALUE self, int genparam)
405
443
}
406
444
}
407
445
408
- return ossl_pkey_new (gen_arg .pkey );
446
+ return ossl_pkey_new (gen_arg .pkey , OSSL_PKEY_HAS_PRIVATE );
409
447
}
410
448
411
449
/*
@@ -527,18 +565,9 @@ GetPrivPKeyPtr(VALUE obj)
527
565
EVP_PKEY * pkey ;
528
566
529
567
GetPKey (obj , pkey );
530
- if (OSSL_PKEY_IS_PRIVATE (obj ))
531
- return pkey ;
532
- /*
533
- * The EVP API does not provide a way to check if the EVP_PKEY has private
534
- * components. Assuming it does...
535
- */
536
- if (!rb_respond_to (obj , id_private_q ))
537
- return pkey ;
538
- if (RTEST (rb_funcallv (obj , id_private_q , 0 , NULL )))
539
- return pkey ;
540
-
541
- rb_raise (rb_eArgError , "private key is needed" );
568
+ if (!ossl_pkey_has (obj , OSSL_PKEY_HAS_PRIVATE ))
569
+ rb_raise (rb_eArgError , "private key is needed" );
570
+ return pkey ;
542
571
}
543
572
544
573
EVP_PKEY *
@@ -614,6 +643,33 @@ ossl_pkey_oid(VALUE self)
614
643
return rb_str_new_cstr (OBJ_nid2sn (nid ));
615
644
}
616
645
646
+ /*
647
+ * call-seq:
648
+ * pkey.public? -> true | false
649
+ *
650
+ * Indicates whether this PKey instance has a public key associated with it or
651
+ * not.
652
+ */
653
+ static VALUE
654
+ ossl_pkey_is_public (VALUE self )
655
+ {
656
+ return ossl_pkey_has (self , OSSL_PKEY_HAS_PUBLIC ) ? Qtrue : Qfalse ;
657
+ }
658
+
659
+ /*
660
+ * call-seq:
661
+ * pkey.private? -> true | false
662
+ *
663
+ * Indicates whether this PKey instance has a private key associated with it or
664
+ * not.
665
+ */
666
+ static VALUE
667
+ ossl_pkey_is_private (VALUE self )
668
+ {
669
+ return ossl_pkey_has (self , OSSL_PKEY_HAS_PRIVATE ) ? Qtrue : Qfalse ;
670
+ }
671
+
672
+
617
673
/*
618
674
* call-seq:
619
675
* pkey.inspect -> string
@@ -1580,6 +1636,8 @@ Init_ossl_pkey(void)
1580
1636
rb_undef_method (cPKey , "initialize_copy" );
1581
1637
#endif
1582
1638
rb_define_method (cPKey , "oid" , ossl_pkey_oid , 0 );
1639
+ rb_define_method (cPKey , "public?" , ossl_pkey_is_public , 0 );
1640
+ rb_define_method (cPKey , "private?" , ossl_pkey_is_private , 0 );
1583
1641
rb_define_method (cPKey , "inspect" , ossl_pkey_inspect , 0 );
1584
1642
rb_define_method (cPKey , "to_text" , ossl_pkey_to_text , 0 );
1585
1643
rb_define_method (cPKey , "private_to_der" , ossl_pkey_private_to_der , -1 );
@@ -1597,7 +1655,7 @@ Init_ossl_pkey(void)
1597
1655
rb_define_method (cPKey , "encrypt" , ossl_pkey_encrypt , -1 );
1598
1656
rb_define_method (cPKey , "decrypt" , ossl_pkey_decrypt , -1 );
1599
1657
1600
- id_private_q = rb_intern ( "private? " );
1658
+ ossl_pkey_feature_id = rb_intern_const ( "state " );
1601
1659
1602
1660
/*
1603
1661
* INIT rsa, dsa, dh, ec
0 commit comments