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