1 /*
2  * 'OpenSSL for Ruby' project
3  * Copyright (C) 2001-2002  Michal Rokos <m.rokos@sh.cvut.cz>
4  * All rights reserved.
5  */
6 /*
7  * This program is licensed under the same licence as Ruby.
8  * (See the file 'LICENCE'.)
9  */
10 #include "ossl.h"
11 
12 #define NewSPKI(klass) \
13     TypedData_Wrap_Struct((klass), &ossl_netscape_spki_type, 0)
14 #define SetSPKI(obj, spki) do { \
15     if (!(spki)) { \
16 	ossl_raise(rb_eRuntimeError, "SPKI wasn't initialized!"); \
17     } \
18     RTYPEDDATA_DATA(obj) = (spki); \
19 } while (0)
20 #define GetSPKI(obj, spki) do { \
21     TypedData_Get_Struct((obj), NETSCAPE_SPKI, &ossl_netscape_spki_type, (spki)); \
22     if (!(spki)) { \
23 	ossl_raise(rb_eRuntimeError, "SPKI wasn't initialized!"); \
24     } \
25 } while (0)
26 
27 /*
28  * Classes
29  */
30 VALUE mNetscape;
31 VALUE cSPKI;
32 VALUE eSPKIError;
33 
34 /*
35  * Public functions
36  */
37 
38 /*
39  * Private functions
40  */
41 
42 static void
ossl_netscape_spki_free(void * spki)43 ossl_netscape_spki_free(void *spki)
44 {
45     NETSCAPE_SPKI_free(spki);
46 }
47 
48 static const rb_data_type_t ossl_netscape_spki_type = {
49     "OpenSSL/NETSCAPE_SPKI",
50     {
51 	0, ossl_netscape_spki_free,
52     },
53     0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
54 };
55 
56 static VALUE
ossl_spki_alloc(VALUE klass)57 ossl_spki_alloc(VALUE klass)
58 {
59     NETSCAPE_SPKI *spki;
60     VALUE obj;
61 
62     obj = NewSPKI(klass);
63     if (!(spki = NETSCAPE_SPKI_new())) {
64 	ossl_raise(eSPKIError, NULL);
65     }
66     SetSPKI(obj, spki);
67 
68     return obj;
69 }
70 
71 /*
72  * call-seq:
73  *    SPKI.new([request]) => spki
74  *
75  * === Parameters
76  * * _request_ - optional raw request, either in PEM or DER format.
77  */
78 static VALUE
ossl_spki_initialize(int argc,VALUE * argv,VALUE self)79 ossl_spki_initialize(int argc, VALUE *argv, VALUE self)
80 {
81     NETSCAPE_SPKI *spki;
82     VALUE buffer;
83     const unsigned char *p;
84 
85     if (rb_scan_args(argc, argv, "01", &buffer) == 0) {
86 	return self;
87     }
88     StringValue(buffer);
89     if (!(spki = NETSCAPE_SPKI_b64_decode(RSTRING_PTR(buffer), RSTRING_LENINT(buffer)))) {
90 	ossl_clear_error();
91 	p = (unsigned char *)RSTRING_PTR(buffer);
92 	if (!(spki = d2i_NETSCAPE_SPKI(NULL, &p, RSTRING_LEN(buffer)))) {
93 	    ossl_raise(eSPKIError, NULL);
94 	}
95     }
96     NETSCAPE_SPKI_free(DATA_PTR(self));
97     SetSPKI(self, spki);
98 
99     return self;
100 }
101 
102 /*
103  * call-seq:
104  *    spki.to_der => DER-encoded string
105  *
106  * Returns the DER encoding of this SPKI.
107  */
108 static VALUE
ossl_spki_to_der(VALUE self)109 ossl_spki_to_der(VALUE self)
110 {
111     NETSCAPE_SPKI *spki;
112     VALUE str;
113     long len;
114     unsigned char *p;
115 
116     GetSPKI(self, spki);
117     if ((len = i2d_NETSCAPE_SPKI(spki, NULL)) <= 0)
118         ossl_raise(eX509CertError, NULL);
119     str = rb_str_new(0, len);
120     p = (unsigned char *)RSTRING_PTR(str);
121     if (i2d_NETSCAPE_SPKI(spki, &p) <= 0)
122         ossl_raise(eX509CertError, NULL);
123     ossl_str_adjust(str, p);
124 
125     return str;
126 }
127 
128 /*
129  * call-seq:
130  *    spki.to_pem => PEM-encoded string
131  *
132  * Returns the PEM encoding of this SPKI.
133  */
134 static VALUE
ossl_spki_to_pem(VALUE self)135 ossl_spki_to_pem(VALUE self)
136 {
137     NETSCAPE_SPKI *spki;
138     char *data;
139     VALUE str;
140 
141     GetSPKI(self, spki);
142     if (!(data = NETSCAPE_SPKI_b64_encode(spki))) {
143 	ossl_raise(eSPKIError, NULL);
144     }
145     str = ossl_buf2str(data, rb_long2int(strlen(data)));
146 
147     return str;
148 }
149 
150 /*
151  * call-seq:
152  *    spki.to_text => string
153  *
154  * Returns a textual representation of this SPKI, useful for debugging
155  * purposes.
156  */
157 static VALUE
ossl_spki_print(VALUE self)158 ossl_spki_print(VALUE self)
159 {
160     NETSCAPE_SPKI *spki;
161     BIO *out;
162 
163     GetSPKI(self, spki);
164     if (!(out = BIO_new(BIO_s_mem()))) {
165 	ossl_raise(eSPKIError, NULL);
166     }
167     if (!NETSCAPE_SPKI_print(out, spki)) {
168 	BIO_free(out);
169 	ossl_raise(eSPKIError, NULL);
170     }
171 
172     return ossl_membio2str(out);
173 }
174 
175 /*
176  * call-seq:
177  *    spki.public_key => pkey
178  *
179  * Returns the public key associated with the SPKI, an instance of
180  * OpenSSL::PKey.
181  */
182 static VALUE
ossl_spki_get_public_key(VALUE self)183 ossl_spki_get_public_key(VALUE self)
184 {
185     NETSCAPE_SPKI *spki;
186     EVP_PKEY *pkey;
187 
188     GetSPKI(self, spki);
189     if (!(pkey = NETSCAPE_SPKI_get_pubkey(spki))) { /* adds an reference */
190 	ossl_raise(eSPKIError, NULL);
191     }
192 
193     return ossl_pkey_new(pkey); /* NO DUP - OK */
194 }
195 
196 /*
197  * call-seq:
198  *    spki.public_key = pub => pkey
199  *
200  * === Parameters
201  * * _pub_ - the public key to be set for this instance
202  *
203  * Sets the public key to be associated with the SPKI, an instance of
204  * OpenSSL::PKey. This should be the public key corresponding to the
205  * private key used for signing the SPKI.
206  */
207 static VALUE
ossl_spki_set_public_key(VALUE self,VALUE key)208 ossl_spki_set_public_key(VALUE self, VALUE key)
209 {
210     NETSCAPE_SPKI *spki;
211     EVP_PKEY *pkey;
212 
213     GetSPKI(self, spki);
214     pkey = GetPKeyPtr(key);
215     ossl_pkey_check_public_key(pkey);
216     if (!NETSCAPE_SPKI_set_pubkey(spki, pkey))
217 	ossl_raise(eSPKIError, "NETSCAPE_SPKI_set_pubkey");
218     return key;
219 }
220 
221 /*
222  * call-seq:
223  *    spki.challenge => string
224  *
225  * Returns the challenge string associated with this SPKI.
226  */
227 static VALUE
ossl_spki_get_challenge(VALUE self)228 ossl_spki_get_challenge(VALUE self)
229 {
230     NETSCAPE_SPKI *spki;
231 
232     GetSPKI(self, spki);
233     if (spki->spkac->challenge->length <= 0) {
234 	OSSL_Debug("Challenge.length <= 0?");
235 	return rb_str_new(0, 0);
236     }
237 
238     return rb_str_new((const char *)spki->spkac->challenge->data,
239 		      spki->spkac->challenge->length);
240 }
241 
242 /*
243  * call-seq:
244  *    spki.challenge = str => string
245  *
246  * === Parameters
247  * * _str_ - the challenge string to be set for this instance
248  *
249  * Sets the challenge to be associated with the SPKI. May be used by the
250  * server, e.g. to prevent replay.
251  */
252 static VALUE
ossl_spki_set_challenge(VALUE self,VALUE str)253 ossl_spki_set_challenge(VALUE self, VALUE str)
254 {
255     NETSCAPE_SPKI *spki;
256 
257     StringValue(str);
258     GetSPKI(self, spki);
259     if (!ASN1_STRING_set(spki->spkac->challenge, RSTRING_PTR(str),
260 			 RSTRING_LENINT(str))) {
261 	ossl_raise(eSPKIError, NULL);
262     }
263 
264     return str;
265 }
266 
267 /*
268  * call-seq:
269  *    spki.sign(key, digest) => spki
270  *
271  * === Parameters
272  * * _key_ - the private key to be used for signing this instance
273  * * _digest_ - the digest to be used for signing this instance
274  *
275  * To sign an SPKI, the private key corresponding to the public key set
276  * for this instance should be used, in addition to a digest algorithm in
277  * the form of an OpenSSL::Digest. The private key should be an instance of
278  * OpenSSL::PKey.
279  */
280 static VALUE
ossl_spki_sign(VALUE self,VALUE key,VALUE digest)281 ossl_spki_sign(VALUE self, VALUE key, VALUE digest)
282 {
283     NETSCAPE_SPKI *spki;
284     EVP_PKEY *pkey;
285     const EVP_MD *md;
286 
287     pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
288     md = ossl_evp_get_digestbyname(digest);
289     GetSPKI(self, spki);
290     if (!NETSCAPE_SPKI_sign(spki, pkey, md)) {
291 	ossl_raise(eSPKIError, NULL);
292     }
293 
294     return self;
295 }
296 
297 /*
298  * call-seq:
299  *    spki.verify(key) => boolean
300  *
301  * === Parameters
302  * * _key_ - the public key to be used for verifying the SPKI signature
303  *
304  * Returns +true+ if the signature is valid, +false+ otherwise. To verify an
305  * SPKI, the public key contained within the SPKI should be used.
306  */
307 static VALUE
ossl_spki_verify(VALUE self,VALUE key)308 ossl_spki_verify(VALUE self, VALUE key)
309 {
310     NETSCAPE_SPKI *spki;
311     EVP_PKEY *pkey;
312 
313     GetSPKI(self, spki);
314     pkey = GetPKeyPtr(key);
315     ossl_pkey_check_public_key(pkey);
316     switch (NETSCAPE_SPKI_verify(spki, pkey)) {
317       case 0:
318 	ossl_clear_error();
319 	return Qfalse;
320       case 1:
321 	return Qtrue;
322       default:
323 	ossl_raise(eSPKIError, "NETSCAPE_SPKI_verify");
324     }
325 }
326 
327 /* Document-class: OpenSSL::Netscape::SPKI
328  *
329  * A Simple Public Key Infrastructure implementation (pronounced "spooky").
330  * The structure is defined as
331  *   PublicKeyAndChallenge ::= SEQUENCE {
332  *     spki SubjectPublicKeyInfo,
333  *     challenge IA5STRING
334  *   }
335  *
336  *   SignedPublicKeyAndChallenge ::= SEQUENCE {
337  *     publicKeyAndChallenge PublicKeyAndChallenge,
338  *     signatureAlgorithm AlgorithmIdentifier,
339  *     signature BIT STRING
340  *   }
341  * where the definitions of SubjectPublicKeyInfo and AlgorithmIdentifier can
342  * be found in RFC5280. SPKI is typically used in browsers for generating
343  * a public/private key pair and a subsequent certificate request, using
344  * the HTML <keygen> element.
345  *
346  * == Examples
347  *
348  * === Creating an SPKI
349  *   key = OpenSSL::PKey::RSA.new 2048
350  *   spki = OpenSSL::Netscape::SPKI.new
351  *   spki.challenge = "RandomChallenge"
352  *   spki.public_key = key.public_key
353  *   spki.sign(key, OpenSSL::Digest::SHA256.new)
354  *   #send a request containing this to a server generating a certificate
355  * === Verifying an SPKI request
356  *   request = #...
357  *   spki = OpenSSL::Netscape::SPKI.new request
358  *   unless spki.verify(spki.public_key)
359  *     # signature is invalid
360  *   end
361  *   #proceed
362  */
363 
364 /* Document-module: OpenSSL::Netscape
365  *
366  * OpenSSL::Netscape is a namespace for SPKI (Simple Public Key
367  * Infrastructure) which implements Signed Public Key and Challenge.
368  * See {RFC 2692}[http://tools.ietf.org/html/rfc2692] and {RFC
369  * 2693}[http://tools.ietf.org/html/rfc2692] for details.
370  */
371 
372 /* Document-class: OpenSSL::Netscape::SPKIError
373  *
374  * Generic Exception class that is raised if an error occurs during an
375  * operation on an instance of OpenSSL::Netscape::SPKI.
376  */
377 
378 void
Init_ossl_ns_spki(void)379 Init_ossl_ns_spki(void)
380 {
381 #if 0
382     mOSSL = rb_define_module("OpenSSL");
383     eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
384 #endif
385 
386     mNetscape = rb_define_module_under(mOSSL, "Netscape");
387 
388     eSPKIError = rb_define_class_under(mNetscape, "SPKIError", eOSSLError);
389 
390     cSPKI = rb_define_class_under(mNetscape, "SPKI", rb_cObject);
391 
392     rb_define_alloc_func(cSPKI, ossl_spki_alloc);
393     rb_define_method(cSPKI, "initialize", ossl_spki_initialize, -1);
394 
395     rb_define_method(cSPKI, "to_der", ossl_spki_to_der, 0);
396     rb_define_method(cSPKI, "to_pem", ossl_spki_to_pem, 0);
397     rb_define_alias(cSPKI, "to_s", "to_pem");
398     rb_define_method(cSPKI, "to_text", ossl_spki_print, 0);
399     rb_define_method(cSPKI, "public_key", ossl_spki_get_public_key, 0);
400     rb_define_method(cSPKI, "public_key=", ossl_spki_set_public_key, 1);
401     rb_define_method(cSPKI, "sign", ossl_spki_sign, 2);
402     rb_define_method(cSPKI, "verify", ossl_spki_verify, 1);
403     rb_define_method(cSPKI, "challenge", ossl_spki_get_challenge, 0);
404     rb_define_method(cSPKI, "challenge=", ossl_spki_set_challenge, 1);
405 }
406