Ruby  3.1.0dev(2021-09-10revisionb76ad15ed0da636161de0243c547ee1e6fc95681)
ossl_ns_spki.c
Go to the documentation of this file.
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  */
33 
34 /*
35  * Public functions
36  */
37 
38 /*
39  * Private functions
40  */
41 
42 static void
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  },
54 };
55 
56 static VALUE
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())) {
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
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)))) {
91  p = (unsigned char *)RSTRING_PTR(buffer);
92  if (!(spki = d2i_NETSCAPE_SPKI(NULL, &p, RSTRING_LEN(buffer)))) {
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
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)
119  str = rb_str_new(0, len);
120  p = (unsigned char *)RSTRING_PTR(str);
121  if (i2d_NETSCAPE_SPKI(spki, &p) <= 0)
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
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))) {
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
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()))) {
166  }
167  if (!NETSCAPE_SPKI_print(out, spki)) {
168  BIO_free(out);
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
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 */
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
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);
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
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
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))) {
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
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)) {
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
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);
316  switch (NETSCAPE_SPKI_verify(spki, pkey)) {
317  case 0:
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.new('SHA256'))
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
380 {
381 #if 0
382  mOSSL = rb_define_module("OpenSSL");
384 #endif
385 
386  mNetscape = rb_define_module_under(mOSSL, "Netscape");
387 
389 
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 }
NewSPKI
#define NewSPKI(klass)
Definition: ossl_ns_spki.c:12
GetPKeyPtr
EVP_PKEY * GetPKeyPtr(VALUE obj)
Definition: ossl_pkey.c:432
Init_ossl_ns_spki
void Init_ossl_ns_spki(void)
Definition: ossl_ns_spki.c:379
StringValue
#define StringValue(v)
Definition: rstring.h:50
rb_define_module_under
VALUE rb_define_module_under(VALUE outer, const char *name)
Definition: class.c:914
eX509CertError
VALUE eX509CertError
Definition: ossl_x509cert.c:31
ossl_membio2str
VALUE ossl_membio2str(BIO *bio)
Definition: ossl_bio.c:29
rb_define_module
VALUE rb_define_module(const char *name)
Definition: class.c:887
ossl.h
argv
char ** argv
Definition: ruby.c:243
ossl_clear_error
void ossl_clear_error(void)
Definition: ossl.c:310
ossl_str_adjust
#define ossl_str_adjust(str, p)
Definition: ossl.h:88
RSTRING_LEN
#define RSTRING_LEN(string)
Definition: fbuffer.h:22
strlen
size_t strlen(const char *)
rb_define_alias
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
Definition: class.c:2050
rb_str_new
#define rb_str_new(str, len)
Definition: string.h:213
ossl_pkey_check_public_key
void ossl_pkey_check_public_key(const EVP_PKEY *pkey)
Definition: ossl_pkey.c:392
RUBY_TYPED_FREE_IMMEDIATELY
@ RUBY_TYPED_FREE_IMMEDIATELY
Definition: rtypeddata.h:62
ossl_buf2str
VALUE ossl_buf2str(char *buf, int len)
Definition: ossl.c:126
rb_long2int
#define rb_long2int
Definition: long.h:62
mOSSL
VALUE mOSSL
Definition: ossl.c:237
rb_define_alloc_func
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
rb_cObject
VALUE rb_cObject
Object class.
Definition: object.c:50
Qfalse
#define Qfalse
Definition: special_consts.h:50
len
uint8_t len
Definition: escape.c:17
ossl_raise
void ossl_raise(VALUE exc, const char *fmt,...)
Definition: ossl.c:299
cSPKI
VALUE cSPKI
Definition: ossl_ns_spki.c:31
NULL
#define NULL
Definition: regenc.h:69
rb_scan_args
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:2347
key
key
Definition: openssl_missing.h:145
Qtrue
#define Qtrue
Definition: special_consts.h:52
VALUE
unsigned long VALUE
Definition: value.h:38
SetSPKI
#define SetSPKI(obj, spki)
Definition: ossl_ns_spki.c:14
RSTRING_PTR
#define RSTRING_PTR(string)
Definition: fbuffer.h:19
str
char str[HTML_ESCAPE_MAX_LEN+1]
Definition: escape.c:18
OSSL_Debug
#define OSSL_Debug(...)
Definition: ossl.h:139
eSPKIError
VALUE eSPKIError
Definition: ossl_ns_spki.c:32
argc
int argc
Definition: ruby.c:242
rb_data_type_struct
Definition: rtypeddata.h:70
eOSSLError
VALUE eOSSLError
Definition: ossl.c:242
GetSPKI
#define GetSPKI(obj, spki)
Definition: ossl_ns_spki.c:20
rb_define_class_under
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition: class.c:809
DATA_PTR
#define DATA_PTR(obj)
Definition: rdata.h:55
rb_define_method
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition: cxxanyargs.hpp:655
mNetscape
VALUE mNetscape
Definition: ossl_ns_spki.c:30
GetPrivPKeyPtr
EVP_PKEY * GetPrivPKeyPtr(VALUE obj)
Definition: ossl_pkey.c:442
ossl_evp_get_digestbyname
const EVP_MD * ossl_evp_get_digestbyname(VALUE obj)
Definition: ossl_digest.c:45
rb_eStandardError
VALUE rb_eStandardError
Definition: error.c:1090
ossl_pkey_new
VALUE ossl_pkey_new(EVP_PKEY *pkey)
Definition: ossl_pkey.c:67