Ruby  3.1.0dev(2021-09-10revisionb76ad15ed0da636161de0243c547ee1e6fc95681)
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  */
24 
25 static VALUE ossl_digest_alloc(VALUE klass);
26 
27 static void
28 ossl_digest_free(void *ctx)
29 {
30  EVP_MD_CTX_destroy(ctx);
31 }
32 
33 static const rb_data_type_t ossl_digest_type = {
34  "OpenSSL/Digest",
35  {
36  0, ossl_digest_free,
37  },
39 };
40 
41 /*
42  * Public
43  */
44 const EVP_MD *
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
73 ossl_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  */
93 static VALUE
94 ossl_digest_alloc(VALUE klass)
95 {
96  return TypedData_Wrap_Struct(klass, &ossl_digest_type, 0);
97 }
98 
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  */
118 static VALUE
119 ossl_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);
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 
143 static VALUE
144 ossl_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)) {
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  */
173 static VALUE
174 ossl_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
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  */
220 static VALUE
221 ossl_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  */
255 static VALUE
256 ossl_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  */
277 static VALUE
278 ossl_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  */
300 static VALUE
301 ossl_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  */
313 void
315 {
316  rb_require("digest");
317 
318 #if 0
319  mOSSL = rb_define_module("OpenSSL");
321 #endif
322 
323  /* Document-class: OpenSSL::Digest
324  *
325  * OpenSSL::Digest allows you to compute message digests (sometimes
326  * interchangeably called "hashes") of arbitrary data that are
327  * cryptographically secure, i.e. a Digest implements a secure one-way
328  * function.
329  *
330  * One-way functions offer some useful properties. E.g. given two
331  * distinct inputs the probability that both yield the same output
332  * is highly unlikely. Combined with the fact that every message digest
333  * algorithm has a fixed-length output of just a few bytes, digests are
334  * often used to create unique identifiers for arbitrary data. A common
335  * example is the creation of a unique id for binary documents that are
336  * stored in a database.
337  *
338  * Another useful characteristic of one-way functions (and thus the name)
339  * is that given a digest there is no indication about the original
340  * data that produced it, i.e. the only way to identify the original input
341  * is to "brute-force" through every possible combination of inputs.
342  *
343  * These characteristics make one-way functions also ideal companions
344  * for public key signature algorithms: instead of signing an entire
345  * document, first a hash of the document is produced with a considerably
346  * faster message digest algorithm and only the few bytes of its output
347  * need to be signed using the slower public key algorithm. To validate
348  * the integrity of a signed document, it suffices to re-compute the hash
349  * and verify that it is equal to that in the signature.
350  *
351  * You can get a list of all digest algorithms supported on your system by
352  * running this command in your terminal:
353  *
354  * openssl list -digest-algorithms
355  *
356  * Among the OpenSSL 1.1.1 supported message digest algorithms are:
357  * * SHA224, SHA256, SHA384, SHA512, SHA512-224 and SHA512-256
358  * * SHA3-224, SHA3-256, SHA3-384 and SHA3-512
359  * * BLAKE2s256 and BLAKE2b512
360  *
361  * Each of these algorithms can be instantiated using the name:
362  *
363  * digest = OpenSSL::Digest.new('SHA256')
364  *
365  * "Breaking" a message digest algorithm means defying its one-way
366  * function characteristics, i.e. producing a collision or finding a way
367  * to get to the original data by means that are more efficient than
368  * brute-forcing etc. Most of the supported digest algorithms can be
369  * considered broken in this sense, even the very popular MD5 and SHA1
370  * algorithms. Should security be your highest concern, then you should
371  * probably rely on SHA224, SHA256, SHA384 or SHA512.
372  *
373  * === Hashing a file
374  *
375  * data = File.binread('document')
376  * sha256 = OpenSSL::Digest.new('SHA256')
377  * digest = sha256.digest(data)
378  *
379  * === Hashing several pieces of data at once
380  *
381  * data1 = File.binread('file1')
382  * data2 = File.binread('file2')
383  * data3 = File.binread('file3')
384  * sha256 = OpenSSL::Digest.new('SHA256')
385  * sha256 << data1
386  * sha256 << data2
387  * sha256 << data3
388  * digest = sha256.digest
389  *
390  * === Reuse a Digest instance
391  *
392  * data1 = File.binread('file1')
393  * sha256 = OpenSSL::Digest.new('SHA256')
394  * digest1 = sha256.digest(data1)
395  *
396  * data2 = File.binread('file2')
397  * sha256.reset
398  * digest2 = sha256.digest(data2)
399  *
400  */
401  cDigest = rb_define_class_under(mOSSL, "Digest", rb_path2class("Digest::Class"));
402  /* Document-class: OpenSSL::Digest::DigestError
403  *
404  * Generic Exception class that is raised if an error occurs during a
405  * Digest operation.
406  */
408 
409  rb_define_alloc_func(cDigest, ossl_digest_alloc);
410 
411  rb_define_method(cDigest, "initialize", ossl_digest_initialize, -1);
412  rb_define_method(cDigest, "initialize_copy", ossl_digest_copy, 1);
413  rb_define_method(cDigest, "reset", ossl_digest_reset, 0);
415  rb_define_alias(cDigest, "<<", "update");
416  rb_define_private_method(cDigest, "finish", ossl_digest_finish, -1);
417  rb_define_method(cDigest, "digest_length", ossl_digest_size, 0);
418  rb_define_method(cDigest, "block_length", ossl_digest_block_length, 0);
419 
420  rb_define_method(cDigest, "name", ossl_digest_name, 0);
421 }
TypedData_Wrap_Struct
#define TypedData_Wrap_Struct(klass, data_type, sval)
Definition: rtypeddata.h:101
StringValue
#define StringValue(v)
Definition: rstring.h:50
Init_ossl_digest
void Init_ossl_digest(void)
Definition: ossl_digest.c:314
PRIsVALUE
#define PRIsVALUE
Definition: inttypes.h:77
rb_str_resize
VALUE rb_str_resize(VALUE, long)
Definition: string.c:2850
rb_define_module
VALUE rb_define_module(const char *name)
Definition: class.c:887
ossl.h
argv
char ** argv
Definition: ruby.c:243
RSTRING_LEN
#define RSTRING_LEN(string)
Definition: fbuffer.h:22
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
RUBY_TYPED_FREE_IMMEDIATELY
@ RUBY_TYPED_FREE_IMMEDIATELY
Definition: rtypeddata.h:62
NIL_P
#define NIL_P
Definition: special_consts.h:46
mOSSL
VALUE mOSSL
Definition: ossl.c:237
INT2NUM
#define INT2NUM
Definition: int.h:43
rb_define_alloc_func
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
ossl_digest_update
VALUE ossl_digest_update(VALUE, VALUE)
Definition: ossl_digest.c:202
cDigest
VALUE cDigest
Definition: ossl_digest.c:22
ossl_raise
void ossl_raise(VALUE exc, const char *fmt,...)
Definition: ossl.c:299
rb_eRuntimeError
VALUE rb_eRuntimeError
Definition: error.c:1091
rb_define_private_method
#define rb_define_private_method(klass, mid, func, arity)
Defines klass#mid and makes it private.
Definition: cxxanyargs.hpp:662
NULL
#define NULL
Definition: regenc.h:69
RTYPEDDATA_DATA
#define RTYPEDDATA_DATA(v)
Definition: rtypeddata.h:47
rb_scan_args
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:2347
GetDigest
#define GetDigest(obj, ctx)
Definition: ossl_digest.c:12
VALUE
unsigned long VALUE
Definition: value.h:38
T_STRING
#define T_STRING
Definition: value_type.h:78
RSTRING_PTR
#define RSTRING_PTR(string)
Definition: fbuffer.h:19
str
char str[HTML_ESCAPE_MAX_LEN+1]
Definition: escape.c:18
ret
return ret
Definition: memory.h:232
rb_check_frozen
#define rb_check_frozen
Definition: error.h:72
argc
int argc
Definition: ruby.c:242
rb_str_new2
#define rb_str_new2
Definition: string.h:276
rb_require
VALUE rb_require(const char *)
Definition: load.c:1186
TypedData_Get_Struct
#define TypedData_Get_Struct(obj, type, data_type, sval)
Definition: rtypeddata.h:130
rb_data_type_struct
Definition: rtypeddata.h:70
rb_path2class
VALUE rb_path2class(const char *)
Definition: variable.c:289
eOSSLError
VALUE eOSSLError
Definition: ossl.c:242
eDigestError
VALUE eDigestError
Definition: ossl_digest.c:23
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
StringValueCStr
#define StringValueCStr(v)
Definition: rstring.h:52
rb_define_method
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition: cxxanyargs.hpp:655
ossl_digest_new
VALUE ossl_digest_new(const EVP_MD *md)
Definition: ossl_digest.c:73
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
EVP_MD_CTX_new
#define EVP_MD_CTX_new
Definition: openssl_missing.h:17
ruby::backward::cxxanyargs::type
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition: cxxanyargs.hpp:56
name
const char * name
Definition: nkf.c:208