Ruby: ext/openssl/ossl_digest.c Source File

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

ossl_digest.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 GetDigest(obj, ctx) do { \
13 TypedData_Get_Struct((obj), EVP_MD_CTX, &ossl_digest_type, (ctx)); \
14 if (!(ctx)) { \
15 ossl_raise(rb_eRuntimeError, "Digest CTX wasn't initialized!"); \
16 } \
17} while (0)
18
19/*
20 * Classes
21 */
22 VALUE cDigest;
23 VALUE eDigestError;
24
25static VALUE ossl_digest_alloc(VALUE klass);
26
27static void
28ossl_digest_free(void *ctx)
29{
30 EVP_MD_CTX_destroy(ctx);
31}
32
33static const rb_data_type_t ossl_digest_type = {
34 "OpenSSL/Digest",
35 {
36 0, ossl_digest_free,
37 },
38 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
39};
40
41/*
42 * Public
43 */
44const EVP_MD *
45ossl_evp_get_digestbyname(VALUE obj)
46{
47 const EVP_MD *md;
48 ASN1_OBJECT *oid = NULL;
49
50 if (RB_TYPE_P(obj, T_STRING)) {
51 const char *name = StringValueCStr(obj);
52
53 md = EVP_get_digestbyname(name);
54 if (!md) {
55 oid = OBJ_txt2obj(name, 0);
56 md = EVP_get_digestbyobj(oid);
57 ASN1_OBJECT_free(oid);
58 }
59 if(!md)
60 ossl_raise(rb_eRuntimeError, "Unsupported digest algorithm (%"PRIsVALUE").", obj);
61 } else {
62 EVP_MD_CTX *ctx;
63
64 GetDigest(obj, ctx);
65
66 md = EVP_MD_CTX_md(ctx);
67 }
68
69 return md;
70}
71
72 VALUE
73ossl_digest_new(const EVP_MD *md)
74{
75 VALUE ret;
76 EVP_MD_CTX *ctx;
77
78 ret = ossl_digest_alloc(cDigest);
79 ctx = EVP_MD_CTX_new();
80 if (!ctx)
81 ossl_raise(eDigestError, "EVP_MD_CTX_new");
82 RTYPEDDATA_DATA(ret) = ctx;
83
84 if (!EVP_DigestInit_ex(ctx, md, NULL))
85 ossl_raise(eDigestError, "Digest initialization failed");
86
87 return ret;
88}
89
90/*
91 * Private
92 */
93static VALUE
94ossl_digest_alloc(VALUE klass)
95{
96 return TypedData_Wrap_Struct(klass, &ossl_digest_type, 0);
97}
98
99 VALUE ossl_digest_update(VALUE, VALUE);
100
101/*
102 * call-seq:
103 * Digest.new(string [, data]) -> Digest
104 *
105 * Creates a Digest instance based on _string_, which is either the ln
106 * (long name) or sn (short name) of a supported digest algorithm.
107 *
108 * If _data_ (a String) is given, it is used as the initial input to the
109 * Digest instance, i.e.
110 *
111 * digest = OpenSSL::Digest.new('sha256', 'digestdata')
112 *
113 * is equivalent to
114 *
115 * digest = OpenSSL::Digest.new('sha256')
116 * digest.update('digestdata')
117 */
118static VALUE
119ossl_digest_initialize(int argc, VALUE *argv, VALUE self)
120{
121 EVP_MD_CTX *ctx;
122 const EVP_MD *md;
123 VALUE type, data;
124
125 rb_scan_args(argc, argv, "11", &type, &data);
126 md = ossl_evp_get_digestbyname(type);
127 if (!NIL_P(data)) StringValue(data);
128
129 TypedData_Get_Struct(self, EVP_MD_CTX, &ossl_digest_type, ctx);
130 if (!ctx) {
131 RTYPEDDATA_DATA(self) = ctx = EVP_MD_CTX_new();
132 if (!ctx)
133 ossl_raise(eDigestError, "EVP_MD_CTX_new");
134 }
135
136 if (!EVP_DigestInit_ex(ctx, md, NULL))
137 ossl_raise(eDigestError, "Digest initialization failed");
138
139 if (!NIL_P(data)) return ossl_digest_update(self, data);
140 return self;
141}
142
143static VALUE
144ossl_digest_copy(VALUE self, VALUE other)
145{
146 EVP_MD_CTX *ctx1, *ctx2;
147
148 rb_check_frozen(self);
149 if (self == other) return self;
150
151 TypedData_Get_Struct(self, EVP_MD_CTX, &ossl_digest_type, ctx1);
152 if (!ctx1) {
153 RTYPEDDATA_DATA(self) = ctx1 = EVP_MD_CTX_new();
154 if (!ctx1)
155 ossl_raise(eDigestError, "EVP_MD_CTX_new");
156 }
157 GetDigest(other, ctx2);
158
159 if (!EVP_MD_CTX_copy(ctx1, ctx2)) {
160 ossl_raise(eDigestError, NULL);
161 }
162 return self;
163}
164
165/*
166 * call-seq:
167 * digest.reset -> self
168 *
169 * Resets the Digest in the sense that any Digest#update that has been
170 * performed is abandoned and the Digest is set to its initial state again.
171 *
172 */
173static VALUE
174ossl_digest_reset(VALUE self)
175{
176 EVP_MD_CTX *ctx;
177
178 GetDigest(self, ctx);
179 if (EVP_DigestInit_ex(ctx, EVP_MD_CTX_md(ctx), NULL) != 1) {
180 ossl_raise(eDigestError, "Digest initialization failed.");
181 }
182
183 return self;
184}
185
186/*
187 * call-seq:
188 * digest.update(string) -> aString
189 *
190 * Not every message digest can be computed in one single pass. If a message
191 * digest is to be computed from several subsequent sources, then each may
192 * be passed individually to the Digest instance.
193 *
194 * === Example
195 * digest = OpenSSL::Digest.new('SHA256')
196 * digest.update('First input')
197 * digest << 'Second input' # equivalent to digest.update('Second input')
198 * result = digest.digest
199 *
200 */
201 VALUE
202ossl_digest_update(VALUE self, VALUE data)
203{
204 EVP_MD_CTX *ctx;
205
206 StringValue(data);
207 GetDigest(self, ctx);
208
209 if (!EVP_DigestUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)))
210 ossl_raise(eDigestError, "EVP_DigestUpdate");
211
212 return self;
213}
214
215/*
216 * call-seq:
217 * digest.finish -> aString
218 *
219 */
220static VALUE
221ossl_digest_finish(int argc, VALUE *argv, VALUE self)
222{
223 EVP_MD_CTX *ctx;
224 VALUE str;
225 int out_len;
226
227 GetDigest(self, ctx);
228 rb_scan_args(argc, argv, "01", &str);
229 out_len = EVP_MD_CTX_size(ctx);
230
231 if (NIL_P(str)) {
232 str = rb_str_new(NULL, out_len);
233 } else {
234 StringValue(str);
235 rb_str_resize(str, out_len);
236 }
237
238 if (!EVP_DigestFinal_ex(ctx, (unsigned char *)RSTRING_PTR(str), NULL))
239 ossl_raise(eDigestError, "EVP_DigestFinal_ex");
240
241 return str;
242}
243
244/*
245 * call-seq:
246 * digest.name -> string
247 *
248 * Returns the sn of this Digest algorithm.
249 *
250 * === Example
251 * digest = OpenSSL::Digest.new('SHA512')
252 * puts digest.name # => SHA512
253 *
254 */
255static VALUE
256ossl_digest_name(VALUE self)
257{
258 EVP_MD_CTX *ctx;
259
260 GetDigest(self, ctx);
261
262 return rb_str_new2(EVP_MD_name(EVP_MD_CTX_md(ctx)));
263}
264
265/*
266 * call-seq:
267 * digest.digest_length -> integer
268 *
269 * Returns the output size of the digest, i.e. the length in bytes of the
270 * final message digest result.
271 *
272 * === Example
273 * digest = OpenSSL::Digest.new('SHA1')
274 * puts digest.digest_length # => 20
275 *
276 */
277static VALUE
278ossl_digest_size(VALUE self)
279{
280 EVP_MD_CTX *ctx;
281
282 GetDigest(self, ctx);
283
284 return INT2NUM(EVP_MD_CTX_size(ctx));
285}
286
287/*
288 * call-seq:
289 * digest.block_length -> integer
290 *
291 * Returns the block length of the digest algorithm, i.e. the length in bytes
292 * of an individual block. Most modern algorithms partition a message to be
293 * digested into a sequence of fix-sized blocks that are processed
294 * consecutively.
295 *
296 * === Example
297 * digest = OpenSSL::Digest.new('SHA1')
298 * puts digest.block_length # => 64
299 */
300static VALUE
301ossl_digest_block_length(VALUE self)
302{
303 EVP_MD_CTX *ctx;
304
305 GetDigest(self, ctx);
306
307 return INT2NUM(EVP_MD_CTX_block_size(ctx));
308}
309
310/*
311 * INIT
312 */
313void
314Init_ossl_digest(void)
315{
316#if 0
317 mOSSL = rb_define_module("OpenSSL");
318 eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
319#endif
320
321 /* Document-class: OpenSSL::Digest
322 *
323 * OpenSSL::Digest allows you to compute message digests (sometimes
324 * interchangeably called "hashes") of arbitrary data that are
325 * cryptographically secure, i.e. a Digest implements a secure one-way
326 * function.
327 *
328 * One-way functions offer some useful properties. E.g. given two
329 * distinct inputs the probability that both yield the same output
330 * is highly unlikely. Combined with the fact that every message digest
331 * algorithm has a fixed-length output of just a few bytes, digests are
332 * often used to create unique identifiers for arbitrary data. A common
333 * example is the creation of a unique id for binary documents that are
334 * stored in a database.
335 *
336 * Another useful characteristic of one-way functions (and thus the name)
337 * is that given a digest there is no indication about the original
338 * data that produced it, i.e. the only way to identify the original input
339 * is to "brute-force" through every possible combination of inputs.
340 *
341 * These characteristics make one-way functions also ideal companions
342 * for public key signature algorithms: instead of signing an entire
343 * document, first a hash of the document is produced with a considerably
344 * faster message digest algorithm and only the few bytes of its output
345 * need to be signed using the slower public key algorithm. To validate
346 * the integrity of a signed document, it suffices to re-compute the hash
347 * and verify that it is equal to that in the signature.
348 *
349 * You can get a list of all digest algorithms supported on your system by
350 * running this command in your terminal:
351 *
352 * openssl list -digest-algorithms
353 *
354 * Among the OpenSSL 1.1.1 supported message digest algorithms are:
355 * * SHA224, SHA256, SHA384, SHA512, SHA512-224 and SHA512-256
356 * * SHA3-224, SHA3-256, SHA3-384 and SHA3-512
357 * * BLAKE2s256 and BLAKE2b512
358 *
359 * Each of these algorithms can be instantiated using the name:
360 *
361 * digest = OpenSSL::Digest.new('SHA256')
362 *
363 * "Breaking" a message digest algorithm means defying its one-way
364 * function characteristics, i.e. producing a collision or finding a way
365 * to get to the original data by means that are more efficient than
366 * brute-forcing etc. Most of the supported digest algorithms can be
367 * considered broken in this sense, even the very popular MD5 and SHA1
368 * algorithms. Should security be your highest concern, then you should
369 * probably rely on SHA224, SHA256, SHA384 or SHA512.
370 *
371 * === Hashing a file
372 *
373 * data = File.read('document')
374 * sha256 = OpenSSL::Digest.new('SHA256')
375 * digest = sha256.digest(data)
376 *
377 * === Hashing several pieces of data at once
378 *
379 * data1 = File.read('file1')
380 * data2 = File.read('file2')
381 * data3 = File.read('file3')
382 * sha256 = OpenSSL::Digest.new('SHA256')
383 * sha256 << data1
384 * sha256 << data2
385 * sha256 << data3
386 * digest = sha256.digest
387 *
388 * === Reuse a Digest instance
389 *
390 * data1 = File.read('file1')
391 * sha256 = OpenSSL::Digest.new('SHA256')
392 * digest1 = sha256.digest(data1)
393 *
394 * data2 = File.read('file2')
395 * sha256.reset
396 * digest2 = sha256.digest(data2)
397 *
398 */
399
400 /*
401 * Digest::Class is defined by the digest library. rb_require() cannot be
402 * used here because it bypasses RubyGems.
403 */
404 rb_funcall(Qnil, rb_intern_const("require"), 1, rb_str_new_cstr("digest"));
405 cDigest = rb_define_class_under(mOSSL, "Digest", rb_path2class("Digest::Class"));
406 /* Document-class: OpenSSL::Digest::DigestError
407 *
408 * Generic Exception class that is raised if an error occurs during a
409 * Digest operation.
410 */
411 eDigestError = rb_define_class_under(cDigest, "DigestError", eOSSLError);
412
413 rb_define_alloc_func(cDigest, ossl_digest_alloc);
414
415 rb_define_method(cDigest, "initialize", ossl_digest_initialize, -1);
416 rb_define_method(cDigest, "initialize_copy", ossl_digest_copy, 1);
417 rb_define_method(cDigest, "reset", ossl_digest_reset, 0);
418 rb_define_method(cDigest, "update", ossl_digest_update, 1);
419 rb_define_alias(cDigest, "<<", "update");
420 rb_define_private_method(cDigest, "finish", ossl_digest_finish, -1);
421 rb_define_method(cDigest, "digest_length", ossl_digest_size, 0);
422 rb_define_method(cDigest, "block_length", ossl_digest_block_length, 0);
423
424 rb_define_method(cDigest, "name", ossl_digest_name, 0);
425}
rb_define_method
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition: cxxanyargs.hpp:653
rb_define_private_method
#define rb_define_private_method(klass, mid, func, arity)
Defines klass#mid and makes it private.
Definition: cxxanyargs.hpp:660
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
PRIsVALUE
#define PRIsVALUE
Definition: function.c:10
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_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_eRuntimeError
VALUE rb_eRuntimeError
Definition: error.c:1055
rb_funcall
VALUE rb_funcall(VALUE, ID, int,...)
Calls a method.
Definition: vm_eval.c:1077
rb_check_frozen
#define rb_check_frozen
Definition: error.h:72
rb_str_new2
#define rb_str_new2
Definition: string.h:276
rb_str_resize
VALUE rb_str_resize(VALUE, long)
Definition: string.c:2859
rb_str_new
#define rb_str_new(str, len)
Definition: string.h:213
rb_str_new_cstr
#define rb_str_new_cstr(str)
Definition: string.h:219
rb_path2class
VALUE rb_path2class(const char *)
Definition: variable.c:287
rb_define_alloc_func
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
INT2NUM
#define INT2NUM
Definition: int.h:43
ruby::backward::cxxanyargs::type
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition: cxxanyargs.hpp:56
name
const char * name
Definition: nkf.c:208
EVP_MD_CTX_new
#define EVP_MD_CTX_new
Definition: openssl_missing.h:50
mOSSL
VALUE mOSSL
Definition: ossl.c:231
ossl_raise
void ossl_raise(VALUE exc, const char *fmt,...)
Definition: ossl.c:293
eOSSLError
VALUE eOSSLError
Definition: ossl.c:236
ossl.h
GetDigest
#define GetDigest(obj, ctx)
Definition: ossl_digest.c:12
ossl_evp_get_digestbyname
const EVP_MD * ossl_evp_get_digestbyname(VALUE obj)
Definition: ossl_digest.c:45
eDigestError
VALUE eDigestError
Definition: ossl_digest.c:23
ossl_digest_update
VALUE ossl_digest_update(VALUE, VALUE)
Definition: ossl_digest.c:202
ossl_digest_new
VALUE ossl_digest_new(const EVP_MD *md)
Definition: ossl_digest.c:73
Init_ossl_digest
void Init_ossl_digest(void)
Definition: ossl_digest.c:314
cDigest
VALUE cDigest
Definition: ossl_digest.c:22
NULL
#define NULL
Definition: regenc.h:69
StringValue
#define StringValue(v)
Definition: rstring.h:50
StringValueCStr
#define StringValueCStr(v)
Definition: rstring.h:52
RTYPEDDATA_DATA
#define RTYPEDDATA_DATA(v)
Definition: rtypeddata.h:47
TypedData_Get_Struct
#define TypedData_Get_Struct(obj, type, data_type, sval)
Definition: rtypeddata.h:130
TypedData_Wrap_Struct
#define TypedData_Wrap_Struct(klass, data_type, sval)
Definition: rtypeddata.h:101
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
Qnil
#define Qnil
Definition: special_consts.h:51
NIL_P
#define NIL_P
Definition: special_consts.h:46
rb_data_type_struct
Definition: rtypeddata.h:70
VALUE
unsigned long VALUE
Definition: value.h:38
T_STRING
#define T_STRING
Definition: value_type.h:77