1/*
2 * $Id: ossl_hmac.c 32609 2011-07-22 04:11:38Z emboss $
3 * 'OpenSSL for Ruby' project
4 * Copyright (C) 2001-2002  Michal Rokos <m.rokos@sh.cvut.cz>
5 * All rights reserved.
6 */
7/*
8 * This program is licenced under the same licence as Ruby.
9 * (See the file 'LICENCE'.)
10 */
11#if !defined(OPENSSL_NO_HMAC)
12
13#include "ossl.h"
14
15#define MakeHMAC(obj, klass, ctx) \
16    (obj) = Data_Make_Struct((klass), HMAC_CTX, 0, ossl_hmac_free, (ctx))
17#define GetHMAC(obj, ctx) do { \
18    Data_Get_Struct((obj), HMAC_CTX, (ctx)); \
19    if (!(ctx)) { \
20	ossl_raise(rb_eRuntimeError, "HMAC wasn't initialized"); \
21    } \
22} while (0)
23#define SafeGetHMAC(obj, ctx) do { \
24    OSSL_Check_Kind((obj), cHMAC); \
25    GetHMAC((obj), (ctx)); \
26} while (0)
27
28/*
29 * Classes
30 */
31VALUE cHMAC;
32VALUE eHMACError;
33
34/*
35 * Public
36 */
37
38/*
39 * Private
40 */
41static void
42ossl_hmac_free(HMAC_CTX *ctx)
43{
44    HMAC_CTX_cleanup(ctx);
45    ruby_xfree(ctx);
46}
47
48static VALUE
49ossl_hmac_alloc(VALUE klass)
50{
51    HMAC_CTX *ctx;
52    VALUE obj;
53
54    MakeHMAC(obj, klass, ctx);
55    HMAC_CTX_init(ctx);
56
57    return obj;
58}
59
60
61/*
62 *  call-seq:
63 *     HMAC.new(key, digest) -> hmac
64 *
65 */
66static VALUE
67ossl_hmac_initialize(VALUE self, VALUE key, VALUE digest)
68{
69    HMAC_CTX *ctx;
70
71    StringValue(key);
72    GetHMAC(self, ctx);
73    HMAC_Init(ctx, RSTRING_PTR(key), RSTRING_LENINT(key),
74		 GetDigestPtr(digest));
75
76    return self;
77}
78
79static VALUE
80ossl_hmac_copy(VALUE self, VALUE other)
81{
82    HMAC_CTX *ctx1, *ctx2;
83
84    rb_check_frozen(self);
85    if (self == other) return self;
86
87    GetHMAC(self, ctx1);
88    SafeGetHMAC(other, ctx2);
89
90    HMAC_CTX_copy(ctx1, ctx2);
91    return self;
92}
93
94/*
95 *  call-seq:
96 *     hmac.update(string) -> self
97 *
98 */
99static VALUE
100ossl_hmac_update(VALUE self, VALUE data)
101{
102    HMAC_CTX *ctx;
103
104    StringValue(data);
105    GetHMAC(self, ctx);
106    HMAC_Update(ctx, (unsigned char *)RSTRING_PTR(data), RSTRING_LEN(data));
107
108    return self;
109}
110
111static void
112hmac_final(HMAC_CTX *ctx, unsigned char **buf, unsigned int *buf_len)
113{
114    HMAC_CTX final;
115
116    HMAC_CTX_copy(&final, ctx);
117    if (!(*buf = OPENSSL_malloc(HMAC_size(&final)))) {
118	HMAC_CTX_cleanup(&final);
119	OSSL_Debug("Allocating %d mem", HMAC_size(&final));
120	ossl_raise(eHMACError, "Cannot allocate memory for hmac");
121    }
122    HMAC_Final(&final, *buf, buf_len);
123    HMAC_CTX_cleanup(&final);
124}
125
126/*
127 *  call-seq:
128 *     hmac.digest -> aString
129 *
130 */
131static VALUE
132ossl_hmac_digest(VALUE self)
133{
134    HMAC_CTX *ctx;
135    unsigned char *buf;
136    unsigned int buf_len;
137    VALUE digest;
138
139    GetHMAC(self, ctx);
140    hmac_final(ctx, &buf, &buf_len);
141    digest = ossl_buf2str((char *)buf, buf_len);
142
143    return digest;
144}
145
146/*
147 *  call-seq:
148 *     hmac.hexdigest -> aString
149 *
150 */
151static VALUE
152ossl_hmac_hexdigest(VALUE self)
153{
154    HMAC_CTX *ctx;
155    unsigned char *buf;
156    char *hexbuf;
157    unsigned int buf_len;
158    VALUE hexdigest;
159
160    GetHMAC(self, ctx);
161    hmac_final(ctx, &buf, &buf_len);
162    if (string2hex(buf, buf_len, &hexbuf, NULL) != 2 * (int)buf_len) {
163	OPENSSL_free(buf);
164	ossl_raise(eHMACError, "Memory alloc error");
165    }
166    OPENSSL_free(buf);
167    hexdigest = ossl_buf2str(hexbuf, 2 * buf_len);
168
169    return hexdigest;
170}
171
172/*
173 *  call-seq:
174 *     hmac.reset -> self
175 *
176 */
177static VALUE
178ossl_hmac_reset(VALUE self)
179{
180    HMAC_CTX *ctx;
181
182    GetHMAC(self, ctx);
183    HMAC_Init(ctx, NULL, 0, NULL);
184
185    return self;
186}
187
188/*
189 *  call-seq:
190 *     HMAC.digest(digest, key, data) -> aString
191 *
192 */
193static VALUE
194ossl_hmac_s_digest(VALUE klass, VALUE digest, VALUE key, VALUE data)
195{
196    unsigned char *buf;
197    unsigned int buf_len;
198
199    StringValue(key);
200    StringValue(data);
201    buf = HMAC(GetDigestPtr(digest), RSTRING_PTR(key), RSTRING_LENINT(key),
202	       (unsigned char *)RSTRING_PTR(data), RSTRING_LEN(data), NULL, &buf_len);
203
204    return rb_str_new((const char *)buf, buf_len);
205}
206
207/*
208 *  call-seq:
209 *     HMAC.digest(digest, key, data) -> aString
210 *
211 */
212static VALUE
213ossl_hmac_s_hexdigest(VALUE klass, VALUE digest, VALUE key, VALUE data)
214{
215    unsigned char *buf;
216    char *hexbuf;
217    unsigned int buf_len;
218    VALUE hexdigest;
219
220    StringValue(key);
221    StringValue(data);
222
223    buf = HMAC(GetDigestPtr(digest), RSTRING_PTR(key), RSTRING_LENINT(key),
224	       (unsigned char *)RSTRING_PTR(data), RSTRING_LEN(data), NULL, &buf_len);
225    if (string2hex(buf, buf_len, &hexbuf, NULL) != 2 * (int)buf_len) {
226	ossl_raise(eHMACError, "Cannot convert buf to hexbuf");
227    }
228    hexdigest = ossl_buf2str(hexbuf, 2 * buf_len);
229
230    return hexdigest;
231}
232
233/*
234 * INIT
235 */
236void
237Init_ossl_hmac()
238{
239#if 0
240    mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */
241#endif
242
243    eHMACError = rb_define_class_under(mOSSL, "HMACError", eOSSLError);
244
245    cHMAC = rb_define_class_under(mOSSL, "HMAC", rb_cObject);
246
247    rb_define_alloc_func(cHMAC, ossl_hmac_alloc);
248    rb_define_singleton_method(cHMAC, "digest", ossl_hmac_s_digest, 3);
249    rb_define_singleton_method(cHMAC, "hexdigest", ossl_hmac_s_hexdigest, 3);
250
251    rb_define_method(cHMAC, "initialize", ossl_hmac_initialize, 2);
252    rb_define_copy_func(cHMAC, ossl_hmac_copy);
253
254    rb_define_method(cHMAC, "reset", ossl_hmac_reset, 0);
255    rb_define_method(cHMAC, "update", ossl_hmac_update, 1);
256    rb_define_alias(cHMAC, "<<", "update");
257    rb_define_method(cHMAC, "digest", ossl_hmac_digest, 0);
258    rb_define_method(cHMAC, "hexdigest", ossl_hmac_hexdigest, 0);
259    rb_define_alias(cHMAC, "inspect", "hexdigest");
260    rb_define_alias(cHMAC, "to_s", "hexdigest");
261}
262
263#else /* NO_HMAC */
264#  warning >>> OpenSSL is compiled without HMAC support <<<
265void
266Init_ossl_hmac()
267{
268    rb_warning("HMAC will NOT be avaible: OpenSSL is compiled without HMAC.");
269}
270#endif /* NO_HMAC */
271