Ruby: ext/openssl/ossl_ns_spki.c Source File

Ruby 3.0.5p211 (2022-11-24 revision ba5cf0f7c52d4d35cc6a173c89eda98ceffa2dcf)

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 */
30 VALUE mNetscape;
31 VALUE cSPKI;
32 VALUE eSPKIError;
33
34/*
35 * Public functions
36 */
37
38/*
39 * Private functions
40 */
41
42static void
43ossl_netscape_spki_free(void *spki)
44{
45 NETSCAPE_SPKI_free(spki);
46}
47
48static 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
56static VALUE
57ossl_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 */
78static VALUE
79ossl_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 */
108static VALUE
109ossl_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 */
134static VALUE
135ossl_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 */
157static VALUE
158ossl_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 */
182static VALUE
183ossl_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 */
207static VALUE
208ossl_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 */
227static VALUE
228ossl_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 */
252static VALUE
253ossl_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 */
280static VALUE
281ossl_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 */
307static VALUE
308ossl_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.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
378void
379Init_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}
rb_define_method
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition: cxxanyargs.hpp:653
out
string_t out
Definition: enough.c:230
len
uint8_t len
Definition: escape.c:17
str
char str[HTML_ESCAPE_MAX_LEN+1]
Definition: escape.c:18
RSTRING_LEN
#define RSTRING_LEN(string)
Definition: fbuffer.h:22
RSTRING_PTR
#define RSTRING_PTR(string)
Definition: fbuffer.h:19
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:797
rb_define_module
VALUE rb_define_module(const char *name)
Definition: class.c:871
rb_define_module_under
VALUE rb_define_module_under(VALUE outer, const char *name)
Definition: class.c:895
rb_define_alias
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
Definition: class.c:1999
rb_scan_args
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:2296
rb_eStandardError
VALUE rb_eStandardError
Definition: error.c:1054
rb_cObject
VALUE rb_cObject
Object class.
Definition: object.c:49
rb_str_new
#define rb_str_new(str, len)
Definition: string.h:213
rb_define_alloc_func
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
rb_long2int
#define rb_long2int
Definition: long.h:62
key
key
Definition: openssl_missing.h:185
mOSSL
VALUE mOSSL
Definition: ossl.c:231
ossl_buf2str
VALUE ossl_buf2str(char *buf, int len)
Definition: ossl.c:120
ossl_raise
void ossl_raise(VALUE exc, const char *fmt,...)
Definition: ossl.c:293
eOSSLError
VALUE eOSSLError
Definition: ossl.c:236
ossl_clear_error
void ossl_clear_error(void)
Definition: ossl.c:304
ossl.h
OSSL_Debug
#define OSSL_Debug(...)
Definition: ossl.h:140
ossl_str_adjust
#define ossl_str_adjust(str, p)
Definition: ossl.h:89
ossl_membio2str
VALUE ossl_membio2str(BIO *bio)
Definition: ossl_bio.c:29
ossl_evp_get_digestbyname
const EVP_MD * ossl_evp_get_digestbyname(VALUE obj)
Definition: ossl_digest.c:45
mNetscape
VALUE mNetscape
Definition: ossl_ns_spki.c:30
cSPKI
VALUE cSPKI
Definition: ossl_ns_spki.c:31
Init_ossl_ns_spki
void Init_ossl_ns_spki(void)
Definition: ossl_ns_spki.c:379
GetSPKI
#define GetSPKI(obj, spki)
Definition: ossl_ns_spki.c:20
NewSPKI
#define NewSPKI(klass)
Definition: ossl_ns_spki.c:12
eSPKIError
VALUE eSPKIError
Definition: ossl_ns_spki.c:32
SetSPKI
#define SetSPKI(obj, spki)
Definition: ossl_ns_spki.c:14
GetPrivPKeyPtr
EVP_PKEY * GetPrivPKeyPtr(VALUE obj)
Definition: ossl_pkey.c:245
ossl_pkey_check_public_key
void ossl_pkey_check_public_key(const EVP_PKEY *pkey)
Definition: ossl_pkey.c:195
GetPKeyPtr
EVP_PKEY * GetPKeyPtr(VALUE obj)
Definition: ossl_pkey.c:235
ossl_pkey_new
VALUE ossl_pkey_new(EVP_PKEY *pkey)
Definition: ossl_pkey.c:129
eX509CertError
VALUE eX509CertError
Definition: ossl_x509cert.c:31
DATA_PTR
#define DATA_PTR(obj)
Definition: rdata.h:56
NULL
#define NULL
Definition: regenc.h:69
StringValue
#define StringValue(v)
Definition: rstring.h:50
RUBY_TYPED_FREE_IMMEDIATELY
@ RUBY_TYPED_FREE_IMMEDIATELY
Definition: rtypeddata.h:62
argc
int argc
Definition: ruby.c:240
argv
char ** argv
Definition: ruby.c:241
Qtrue
#define Qtrue
Definition: special_consts.h:52
Qfalse
#define Qfalse
Definition: special_consts.h:50
strlen
size_t strlen(const char *)
rb_data_type_struct
Definition: rtypeddata.h:70
VALUE
unsigned long VALUE
Definition: value.h:38