Ruby 1.9.3p327(2012-11-10revision37606)
ext/openssl/ossl_digest.c
Go to the documentation of this file.
00001 /*
00002  * $Id: ossl_digest.c 34505 2012-02-09 03:25:07Z nobu $
00003  * 'OpenSSL for Ruby' project
00004  * Copyright (C) 2001-2002  Michal Rokos <m.rokos@sh.cvut.cz>
00005  * All rights reserved.
00006  */
00007 /*
00008  * This program is licenced under the same licence as Ruby.
00009  * (See the file 'LICENCE'.)
00010  */
00011 #include "ossl.h"
00012 
00013 #define GetDigest(obj, ctx) do { \
00014     Data_Get_Struct((obj), EVP_MD_CTX, (ctx)); \
00015     if (!(ctx)) { \
00016         ossl_raise(rb_eRuntimeError, "Digest CTX wasn't initialized!"); \
00017     } \
00018 } while (0)
00019 #define SafeGetDigest(obj, ctx) do { \
00020     OSSL_Check_Kind((obj), cDigest); \
00021     GetDigest((obj), (ctx)); \
00022 } while (0)
00023 
00024 /*
00025  * Classes
00026  */
00027 VALUE cDigest;
00028 VALUE eDigestError;
00029 
00030 static VALUE ossl_digest_alloc(VALUE klass);
00031 
00032 /*
00033  * Public
00034  */
00035 const EVP_MD *
00036 GetDigestPtr(VALUE obj)
00037 {
00038     const EVP_MD *md;
00039     ASN1_OBJECT *oid = NULL;
00040 
00041     if (TYPE(obj) == T_STRING) {
00042         const char *name = StringValueCStr(obj);
00043 
00044         md = EVP_get_digestbyname(name);
00045         if (!md) {
00046             oid = OBJ_txt2obj(name, 0);
00047             md = EVP_get_digestbyobj(oid);
00048             ASN1_OBJECT_free(oid);
00049         }
00050         if(!md)
00051             ossl_raise(rb_eRuntimeError, "Unsupported digest algorithm (%s).", name);
00052     } else {
00053         EVP_MD_CTX *ctx;
00054 
00055         SafeGetDigest(obj, ctx);
00056 
00057         md = EVP_MD_CTX_md(ctx);
00058     }
00059 
00060     return md;
00061 }
00062 
00063 VALUE
00064 ossl_digest_new(const EVP_MD *md)
00065 {
00066     VALUE ret;
00067     EVP_MD_CTX *ctx;
00068 
00069     ret = ossl_digest_alloc(cDigest);
00070     GetDigest(ret, ctx);
00071     if (EVP_DigestInit_ex(ctx, md, NULL) != 1) {
00072         ossl_raise(eDigestError, "Digest initialization failed.");
00073     }
00074 
00075     return ret;
00076 }
00077 
00078 /*
00079  * Private
00080  */
00081 static VALUE
00082 ossl_digest_alloc(VALUE klass)
00083 {
00084     EVP_MD_CTX *ctx;
00085     VALUE obj;
00086 
00087     ctx = EVP_MD_CTX_create();
00088     if (ctx == NULL)
00089         ossl_raise(rb_eRuntimeError, "EVP_MD_CTX_create() failed");
00090     obj = Data_Wrap_Struct(klass, 0, EVP_MD_CTX_destroy, ctx);
00091 
00092     return obj;
00093 }
00094 
00095 VALUE ossl_digest_update(VALUE, VALUE);
00096 
00097 /*
00098  *  call-seq:
00099  *     Digest.new(string [, data]) -> Digest
00100  *
00101  * Creates a Digest instance based on +string+, which is either the ln
00102  * (long name) or sn (short name) of a supported digest algorithm.
00103  * If +data+ (a +String+) is given, it is used as the initial input to the
00104  * Digest instance, i.e.
00105  *   digest = OpenSSL::Digest.new('sha256', 'digestdata')
00106  * is equal to
00107  *   digest = OpenSSL::Digest.new('sha256')
00108  *   digest.update('digestdata')
00109  *
00110  * === Example
00111  *   digest = OpenSSL::Digest.new('sha1')
00112  *
00113  *
00114  */
00115 static VALUE
00116 ossl_digest_initialize(int argc, VALUE *argv, VALUE self)
00117 {
00118     EVP_MD_CTX *ctx;
00119     const EVP_MD *md;
00120     VALUE type, data;
00121 
00122     rb_scan_args(argc, argv, "11", &type, &data);
00123     md = GetDigestPtr(type);
00124     if (!NIL_P(data)) StringValue(data);
00125 
00126     GetDigest(self, ctx);
00127     if (EVP_DigestInit_ex(ctx, md, NULL) != 1) {
00128         ossl_raise(eDigestError, "Digest initialization failed.");
00129     }
00130 
00131     if (!NIL_P(data)) return ossl_digest_update(self, data);
00132     return self;
00133 }
00134 
00135 static VALUE
00136 ossl_digest_copy(VALUE self, VALUE other)
00137 {
00138     EVP_MD_CTX *ctx1, *ctx2;
00139 
00140     rb_check_frozen(self);
00141     if (self == other) return self;
00142 
00143     GetDigest(self, ctx1);
00144     SafeGetDigest(other, ctx2);
00145 
00146     if (!EVP_MD_CTX_copy(ctx1, ctx2)) {
00147         ossl_raise(eDigestError, NULL);
00148     }
00149     return self;
00150 }
00151 
00152 /*
00153  *  call-seq:
00154  *     digest.reset -> self
00155  *
00156  * Resets the Digest in the sense that any Digest#update that has been
00157  * performed is abandoned and the Digest is set to its initial state again.
00158  *
00159  */
00160 static VALUE
00161 ossl_digest_reset(VALUE self)
00162 {
00163     EVP_MD_CTX *ctx;
00164 
00165     GetDigest(self, ctx);
00166     if (EVP_DigestInit_ex(ctx, EVP_MD_CTX_md(ctx), NULL) != 1) {
00167         ossl_raise(eDigestError, "Digest initialization failed.");
00168     }
00169 
00170     return self;
00171 }
00172 
00173 /*
00174  *  call-seq:
00175  *     digest.update(string) -> aString
00176  *
00177  * Not every message digest can be computed in one single pass. If a message
00178  * digest is to be computed from several subsequent sources, then each may
00179  * be passed individually to the Digest instance.
00180  *
00181  * === Example
00182  *   digest = OpenSSL::Digest::SHA256.new
00183  *   digest.update('First input')
00184  *   digest << 'Second input' # equivalent to digest.update('Second input')
00185  *   result = digest.digest
00186  *
00187  */
00188 VALUE
00189 ossl_digest_update(VALUE self, VALUE data)
00190 {
00191     EVP_MD_CTX *ctx;
00192 
00193     StringValue(data);
00194     GetDigest(self, ctx);
00195     EVP_DigestUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data));
00196 
00197     return self;
00198 }
00199 
00200 /*
00201  *  call-seq:
00202  *      digest.finish -> aString
00203  *
00204  */
00205 static VALUE
00206 ossl_digest_finish(int argc, VALUE *argv, VALUE self)
00207 {
00208     EVP_MD_CTX *ctx;
00209     VALUE str;
00210 
00211     rb_scan_args(argc, argv, "01", &str);
00212 
00213     GetDigest(self, ctx);
00214 
00215     if (NIL_P(str)) {
00216         str = rb_str_new(NULL, EVP_MD_CTX_size(ctx));
00217     } else {
00218         StringValue(str);
00219         rb_str_resize(str, EVP_MD_CTX_size(ctx));
00220     }
00221 
00222     EVP_DigestFinal_ex(ctx, (unsigned char *)RSTRING_PTR(str), NULL);
00223 
00224     return str;
00225 }
00226 
00227 /*
00228  *  call-seq:
00229  *      digest.name -> string
00230  *
00231  * Returns the sn of this Digest instance.
00232  *
00233  * === Example
00234  *   digest = OpenSSL::Digest::SHA512.new
00235  *   puts digest.name # => SHA512
00236  *
00237  */
00238 static VALUE
00239 ossl_digest_name(VALUE self)
00240 {
00241     EVP_MD_CTX *ctx;
00242 
00243     GetDigest(self, ctx);
00244 
00245     return rb_str_new2(EVP_MD_name(EVP_MD_CTX_md(ctx)));
00246 }
00247 
00248 /*
00249  *  call-seq:
00250  *      digest.digest_length -> integer
00251  *
00252  * Returns the output size of the digest, i.e. the length in bytes of the
00253  * final message digest result.
00254  *
00255  * === Example
00256  *   digest = OpenSSL::Digest::SHA1.new
00257  *   puts digest.digest_length # => 20
00258  *
00259  */
00260 static VALUE
00261 ossl_digest_size(VALUE self)
00262 {
00263     EVP_MD_CTX *ctx;
00264 
00265     GetDigest(self, ctx);
00266 
00267     return INT2NUM(EVP_MD_CTX_size(ctx));
00268 }
00269 
00270 /*
00271  *  call-seq:
00272  *      digest.block_length -> integer
00273  *
00274  * Returns the block length of the digest algorithm, i.e. the length in bytes
00275  * of an individual block. Most modern algorithms partition a message to be
00276  * digested into a sequence of fix-sized blocks that are processed
00277  * consecutively.
00278  *
00279  * === Example
00280  *   digest = OpenSSL::Digest::SHA1.new
00281  *   puts digest.block_length # => 64
00282  */
00283 static VALUE
00284 ossl_digest_block_length(VALUE self)
00285 {
00286     EVP_MD_CTX *ctx;
00287 
00288     GetDigest(self, ctx);
00289 
00290     return INT2NUM(EVP_MD_CTX_block_size(ctx));
00291 }
00292 
00293 /*
00294  * INIT
00295  */
00296 void
00297 Init_ossl_digest()
00298 {
00299     rb_require("digest");
00300 
00301 #if 0
00302     mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */
00303 #endif
00304 
00305     /* Document-class: OpenSSL::Digest
00306      *
00307      * OpenSSL::Digest allows you to compute message digests (sometimes
00308      * interchangeably called "hashes") of arbitrary data that are
00309      * cryptographically secure, i.e. a Digest implements a secure one-way
00310      * function.
00311      *
00312      * One-way functions offer some useful properties. E.g. given two
00313      * distinct inputs the probability that both yield the same output
00314      * is highly unlikely. Combined with the fact that every message digest
00315      * algorithm has a fixed-length output of just a few bytes, digests are
00316      * often used to create unique identifiers for arbitrary data. A common
00317      * example is the creation of a unique id for binary documents that are
00318      * stored in a database.
00319      *
00320      * Another useful characteristic of one-way functions (and thus the name)
00321      * is that given a digest there is no indication about the original
00322      * data that produced it, i.e. the only way to identify the original input
00323      * is to "brute-force" through every possible combination of inputs.
00324      *
00325      * These characteristics make one-way functions also ideal companions
00326      * for public key signature algorithms: instead of signing an entire
00327      * document, first a hash of the document is produced with a considerably
00328      * faster message digest algorithm and only the few bytes of its output
00329      * need to be signed using the slower public key algorithm. To validate
00330      * the integrity of a signed document, it suffices to re-compute the hash
00331      * and verify that it is equal to that in the signature.
00332      *
00333      * Among the supported message digest algorithms are:
00334      * * SHA, SHA1, SHA224, SHA256, SHA384 and SHA512
00335      * * MD2, MD4, MDC2 and MD5
00336      * * RIPEMD160
00337      * * DSS, DSS1 (Pseudo algorithms to be used for DSA signatures. DSS is
00338      *   equal to SHA and DSS1 is equal to SHA1)
00339      *
00340      * For each of these algorithms, there is a sub-class of Digest that
00341      * can be instantiated as simply as e.g.
00342      *
00343      *   digest = OpenSSL::Digest::SHA1.new
00344      *
00345      * === Mapping between Digest class and sn/ln
00346      *
00347      * The sn (short names) and ln (long names) are defined in
00348      * <openssl/object.h> and <openssl/obj_mac.h>. They are textual
00349      * representations of ASN.1 OBJECT IDENTIFIERs. Each supported digest
00350      * algorithm has an OBJECT IDENTIFIER associated to it and those again
00351      * have short/long names assigned to them.
00352      * E.g. the OBJECT IDENTIFIER for SHA-1 is 1.3.14.3.2.26 and its
00353      * sn is "SHA1" and its ln is "sha1".
00354      * ==== MD2
00355      * * sn: MD2
00356      * * ln: md2
00357      * ==== MD4
00358      * * sn: MD4
00359      * * ln: md4
00360      * ==== MD5
00361      * * sn: MD5
00362      * * ln: md5
00363      * ==== SHA
00364      * * sn: SHA
00365      * * ln: SHA
00366      * ==== SHA-1
00367      * * sn: SHA1
00368      * * ln: sha1
00369      * ==== SHA-224
00370      * * sn: SHA224
00371      * * ln: sha224
00372      * ==== SHA-256
00373      * * sn: SHA256
00374      * * ln: sha256
00375      * ==== SHA-384
00376      * * sn: SHA384
00377      * * ln: sha384
00378      * ==== SHA-512
00379      * * sn: SHA512
00380      * * ln: sha512
00381      *
00382      * "Breaking" a message digest algorithm means defying its one-way
00383      * function characteristics, i.e. producing a collision or finding a way
00384      * to get to the original data by means that are more efficient than
00385      * brute-forcing etc. Most of the supported digest algorithms can be
00386      * considered broken in this sense, even the very popular MD5 and SHA1
00387      * algorithms. Should security be your highest concern, then you should
00388      * probably rely on SHA224, SHA256, SHA384 or SHA512.
00389      *
00390      * === Hashing a file
00391      *
00392      *   data = File.read('document')
00393      *   sha256 = OpenSSL::Digest::SHA256.new
00394      *   digest = sha256.digest(data)
00395      *
00396      * === Hashing several pieces of data at once
00397      *
00398      *   data1 = File.read('file1')
00399      *   data2 = File.read('file2')
00400      *   data3 = File.read('file3')
00401      *   sha256 = OpenSSL::Digest::SHA256.new
00402      *   sha256 << data1
00403      *   sha256 << data2
00404      *   sha256 << data3
00405      *   digest = sha256.digest
00406      *
00407      * === Reuse a Digest instance
00408      *
00409      *   data1 = File.read('file1')
00410      *   sha256 = OpenSSL::Digest::SHA256.new
00411      *   digest1 = sha256.digest(data1)
00412      *
00413      *   data2 = File.read('file2')
00414      *   sha256.reset
00415      *   digest2 = sha256.digest(data2)
00416      *
00417      */
00418     cDigest = rb_define_class_under(mOSSL, "Digest", rb_path2class("Digest::Class"));
00419     /* Document-class: OpenSSL::Digest::DigestError
00420      *
00421      * Generic Exception class that is raised if an error occurs during a
00422      * Digest operation.
00423      */
00424     eDigestError = rb_define_class_under(cDigest, "DigestError", eOSSLError);
00425 
00426     rb_define_alloc_func(cDigest, ossl_digest_alloc);
00427 
00428     rb_define_method(cDigest, "initialize", ossl_digest_initialize, -1);
00429     rb_define_copy_func(cDigest, ossl_digest_copy);
00430     rb_define_method(cDigest, "reset", ossl_digest_reset, 0);
00431     rb_define_method(cDigest, "update", ossl_digest_update, 1);
00432     rb_define_alias(cDigest, "<<", "update");
00433     rb_define_private_method(cDigest, "finish", ossl_digest_finish, -1);
00434     rb_define_method(cDigest, "digest_length", ossl_digest_size, 0);
00435     rb_define_method(cDigest, "block_length", ossl_digest_block_length, 0);
00436 
00437     rb_define_method(cDigest, "name", ossl_digest_name, 0);
00438 }
00439