]> git.sur5r.net Git - openldap/blob - contrib/slapd-modules/passwd/totp/slapd-totp.c
Merge remote-tracking branch 'origin/mdb.RE/0.9'
[openldap] / contrib / slapd-modules / passwd / totp / slapd-totp.c
1 /* slapd-totp.c - Password module and overlay for TOTP */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2015 The OpenLDAP Foundation.
6  * Portions Copyright 2015 by Howard Chu, Symas Corp.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work includes code from the lastbind overlay.
19  */
20
21 #include <portable.h>
22
23 #include <lber.h>
24 #include <lber_pvt.h>
25 #include "lutil.h"
26 #include <ac/stdlib.h>
27 #include <ac/ctype.h>
28 #include <ac/string.h>
29 /* include socket.h to get sys/types.h and/or winsock2.h */
30 #include <ac/socket.h>
31
32 #include <openssl/sha.h>
33 #include <openssl/hmac.h>
34
35 #include "slap.h"
36 #include "config.h"
37
38 static LUTIL_PASSWD_CHK_FUNC chk_totp1, chk_totp256, chk_totp512;
39 static LUTIL_PASSWD_HASH_FUNC hash_totp1, hash_totp256, hash_totp512;
40 static const struct berval scheme_totp1 = BER_BVC("{TOTP1}");
41 static const struct berval scheme_totp256 = BER_BVC("{TOTP256}");
42 static const struct berval scheme_totp512 = BER_BVC("{TOTP512}");
43
44 static AttributeDescription *ad_authTimestamp;
45
46 /* This is the definition used by ISODE, as supplied to us in
47  * ITS#6238 Followup #9
48  */
49 static struct schema_info {
50         char *def;
51         AttributeDescription **ad;
52 } totp_OpSchema[] = {
53         {       "( 1.3.6.1.4.1.453.16.2.188 "
54                 "NAME 'authTimestamp' "
55                 "DESC 'last successful authentication using any method/mech' "
56                 "EQUALITY generalizedTimeMatch "
57                 "ORDERING generalizedTimeOrderingMatch "
58                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
59                 "SINGLE-VALUE NO-USER-MODIFICATION USAGE dsaOperation )",
60                 &ad_authTimestamp},
61         { NULL, NULL }
62 };
63
64 /* RFC3548 base32 encoding/decoding */
65
66 static const char Base32[] =
67         "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
68 static const char Pad32 = '=';
69
70 static int
71 totp_b32_ntop(
72         u_char const *src,
73         size_t srclength,
74         char *target,
75         size_t targsize)
76 {
77         size_t datalength = 0;
78         u_char input0;
79         u_int input1;   /* assumed to be at least 32 bits */
80         u_char output[8];
81         int i;
82
83         while (4 < srclength) {
84                 if (datalength + 8 > targsize)
85                         return (-1);
86                 input0 = *src++;
87                 input1 = *src++;
88                 input1 <<= 8;
89                 input1 |= *src++;
90                 input1 <<= 8;
91                 input1 |= *src++;
92                 input1 <<= 8;
93                 input1 |= *src++;
94                 srclength -= 5;
95
96                 for (i=7; i>1; i--) {
97                         output[i] = input1 & 0x1f;
98                         input1 >>= 5;
99                 }
100                 output[0] = input0 >> 3;
101                 output[1] = (input0 & 0x07) << 2 | input1;
102
103                 for (i=0; i<8; i++)
104                         target[datalength++] = Base32[output[i]];
105         }
106     
107         /* Now we worry about padding. */
108         if (0 != srclength) {
109                 static const int outlen[] = { 2,4,5,7 };
110                 int n;
111                 if (datalength + 8 > targsize)
112                         return (-1);
113
114                 /* Get what's left. */
115                 input1 = *src++;
116                 for (i = 1; i < srclength; i++) {
117                         input1 <<= 8;
118                         input1 |= *src++;
119                 }
120                 input1 <<= 8 * (4-srclength);
121                 n = outlen[srclength-1];
122                 for (i=0; i<n; i++) {
123                         target[datalength++] = Base32[(input1 & 0xf8000000) >> 27];
124                         input1 <<= 5;
125                 }
126                 for (; i<8; i++)
127                         target[datalength++] = Pad32;
128         }
129         if (datalength >= targsize)
130                 return (-1);
131         target[datalength] = '\0';      /* Returned value doesn't count \0. */
132         return (datalength);
133 }
134
135 /* converts characters, eight at a time, starting at src
136    from base - 32 numbers into five 8 bit bytes in the target area.
137    it returns the number of data bytes stored at the target, or -1 on error.
138  */
139
140 static int
141 totp_b32_pton(
142         char const *src,
143         u_char *target, 
144         size_t targsize)
145 {
146         int tarindex, state, ch;
147         char *pos;
148
149         state = 0;
150         tarindex = 0;
151
152         while ((ch = *src++) != '\0') {
153                 if (ch == Pad32)
154                         break;
155
156                 pos = strchr(Base32, ch);
157                 if (pos == 0)           /* A non-base32 character. */
158                         return (-1);
159
160                 switch (state) {
161                 case 0:
162                         if (target) {
163                                 if ((size_t)tarindex >= targsize)
164                                         return (-1);
165                                 target[tarindex] = (pos - Base32) << 3;
166                         }
167                         state = 1;
168                         break;
169                 case 1:
170                         if (target) {
171                                 if ((size_t)tarindex + 1 >= targsize)
172                                         return (-1);
173                                 target[tarindex]   |=  (pos - Base32) >> 2;
174                                 target[tarindex+1]  = ((pos - Base32) & 0x3)
175                                                         << 6 ;
176                         }
177                         tarindex++;
178                         state = 2;
179                         break;
180                 case 2:
181                         if (target) {
182                                 target[tarindex]   |=  (pos - Base32) << 1;
183                         }
184                         state = 3;
185                         break;
186                 case 3:
187                         if (target) {
188                                 if ((size_t)tarindex + 1 >= targsize)
189                                         return (-1);
190                                 target[tarindex] |= (pos - Base32) >> 4;
191                                 target[tarindex+1]  = ((pos - Base32) & 0xf)
192                                                         << 4 ;
193                         }
194                         tarindex++;
195                         state = 4;
196                         break;
197                 case 4:
198                         if (target) {
199                                 if ((size_t)tarindex + 1 >= targsize)
200                                         return (-1);
201                                 target[tarindex] |= (pos - Base32) >> 1;
202                                 target[tarindex+1]  = ((pos - Base32) & 0x1)
203                                                         << 7 ;
204                         }
205                         tarindex++;
206                         state = 5;
207                         break;
208                 case 5:
209                         if (target) {
210                                 target[tarindex]   |=  (pos - Base32) << 2;
211                         }
212                         state = 6;
213                         break;
214                 case 6:
215                         if (target) {
216                                 if ((size_t)tarindex + 1 >= targsize)
217                                         return (-1);
218                                 target[tarindex] |= (pos - Base32) >> 3;
219                                 target[tarindex+1]  = ((pos - Base32) & 0x7)
220                                                         << 5 ;
221                         }
222                         tarindex++;
223                         state = 7;
224                         break;
225                 case 7:
226                         if (target) {
227                                 target[tarindex]   |=  (pos - Base32);
228                         }
229                         state = 0;
230                         tarindex++;
231                         break;
232
233                 default:
234                         abort();
235                 }
236         }
237
238         /*
239          * We are done decoding Base-32 chars.  Let's see if we ended
240          * on a byte boundary, and/or with erroneous trailing characters.
241          */
242
243         if (ch == Pad32) {              /* We got a pad char. */
244                 int i = 1;
245
246                 /* count pad chars */
247                 for (; ch; ch = *src++) {
248                         if (ch != Pad32)
249                                 return (-1);
250                         i++;
251                 }
252                 /* there are only 4 valid ending states with a
253                  * pad character, make sure the number of pads is valid.
254                  */
255                 switch(state) {
256                 case 2: if (i != 6) return -1;
257                         break;
258                 case 4: if (i != 4) return -1;
259                         break;
260                 case 5: if (i != 3) return -1;
261                         break;
262                 case 7: if (i != 1) return -1;
263                         break;
264                 default:
265                         return -1;
266                 }
267                 /*
268                  * Now make sure that the "extra" bits that slopped past
269                  * the last full byte were zeros.  If we don't check them,
270                  * they become a subliminal channel.
271                  */
272                 if (target && target[tarindex] != 0)
273                         return (-1);
274         } else {
275                 /*
276                  * We ended by seeing the end of the string.  Make sure we
277                  * have no partial bytes lying around.
278                  */
279                 if (state != 0)
280                         return (-1);
281         }
282
283         return (tarindex);
284 }
285
286 /* RFC6238 TOTP */
287
288 #define HMAC_setup(ctx, key, len, hash) HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, key, len, hash, 0)
289 #define HMAC_crunch(ctx, buf, len)      HMAC_Update(&ctx, buf, len)
290 #define HMAC_finish(ctx, dig, dlen)     HMAC_Final(&ctx, dig, &dlen); HMAC_CTX_cleanup(&ctx)
291
292 typedef struct myval {
293         ber_len_t mv_len;
294         void *mv_val;
295 } myval;
296
297 static void do_hmac(
298         const void *hash,
299         myval *key,
300         myval *data,
301         myval *out)
302 {
303         HMAC_CTX ctx;
304         unsigned int digestLen;
305
306         HMAC_setup(ctx, key->mv_val, key->mv_len, hash);
307         HMAC_crunch(ctx, data->mv_val, data->mv_len);
308         HMAC_finish(ctx, out->mv_val, digestLen);
309         out->mv_len = digestLen;
310 }
311
312 static const int DIGITS_POWER[] = {
313         1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
314
315 static void generate(
316         myval *key,
317         unsigned long tval,
318         int digits,
319         myval *out,
320         const void *mech)
321 {
322         unsigned char digest[SHA512_DIGEST_LENGTH];
323         myval digval;
324         myval data;
325         unsigned char msg[8];
326         int i, offset, res, otp;
327
328 #if !WORDS_BIGENDIAN
329         /* only needed on little-endian, can just use tval directly on big-endian */
330         for (i=7; i>=0; i--) {
331                 msg[i] = tval & 0xff;
332                 tval >>= 8;
333         }
334 #endif
335
336         data.mv_val = msg;
337         data.mv_len = sizeof(msg);
338
339         digval.mv_val = digest;
340         digval.mv_len = sizeof(digest);
341         do_hmac(mech, key, &data, &digval);
342
343         offset = digest[digval.mv_len-1] & 0xf;
344         res = ((digest[offset] & 0x7f) << 24) |
345                         ((digest[offset+1] & 0xff) << 16) |
346                         ((digest[offset+2] & 0xff) << 8) |
347                         (digest[offset+3] & 0xff);
348
349         otp = res % DIGITS_POWER[digits];
350         out->mv_len = snprintf(out->mv_val, out->mv_len, "%0*d", digits, otp);
351 }
352
353 static int totp_op_cleanup( Operation *op, SlapReply *rs );
354 static int totp_bind_response( Operation *op, SlapReply *rs );
355
356 #define TIME_STEP       30
357 #define DIGITS  6
358
359 static int chk_totp(
360         const struct berval *passwd,
361         const struct berval *cred,
362         const void *mech,
363         const char **text)
364 {
365         void *ctx, *op_tmp;
366         Operation *op;
367         Entry *e;
368         Attribute *a;
369         long t = time(0L) / TIME_STEP;
370         int rc;
371         myval out, key;
372         char outbuf[32];
373
374         /* Find our thread context, find our Operation */
375         ctx = ldap_pvt_thread_pool_context();
376         if (ldap_pvt_thread_pool_getkey(ctx, totp_op_cleanup, &op_tmp, NULL) ||
377                 !op_tmp)
378                 return LUTIL_PASSWD_ERR;
379         op = op_tmp;
380
381         rc = be_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &e);
382         if (rc != LDAP_SUCCESS) return LUTIL_PASSWD_ERR;
383
384         /* Make sure previous login is older than current time */
385         a = attr_find(e->e_attrs, ad_authTimestamp);
386         if (a) {
387                 struct lutil_tm tm;
388                 struct lutil_timet tt;
389                 if (lutil_parsetime(a->a_vals[0].bv_val, &tm) == 0 &&
390                         lutil_tm2time(&tm, &tt) == 0) {
391                         long told = tt.tt_sec / TIME_STEP;
392                         if (told >= t)
393                                 rc = LUTIL_PASSWD_ERR;
394                 }
395                 if (!rc) {      /* seems OK, remember old stamp */
396                         slap_callback *sc;
397                         for (sc = op->o_callback; sc; sc = sc->sc_next) {
398                                 if (sc->sc_response == totp_bind_response) {
399                                         sc->sc_private = ber_dupbv_x(NULL, &a->a_vals[0], op->o_tmpmemctx);
400                                         break;
401                                 }
402                         }
403                 }
404         }       /* else no previous login, 1st use is OK */
405
406         be_entry_release_r(op, e);
407         if (rc) return rc;
408
409         /* Key is stored in base32 */
410         key.mv_len = passwd->bv_len * 5 / 8;
411         key.mv_val = ber_memalloc(key.mv_len+1);
412
413         if (!key.mv_val)
414                 return LUTIL_PASSWD_ERR;
415
416         rc = totp_b32_pton(passwd->bv_val, key.mv_val, key.mv_len);
417         if (rc < 1) {
418                 rc = LUTIL_PASSWD_ERR;
419                 goto out;
420         }
421
422         out.mv_val = outbuf;
423         out.mv_len = sizeof(outbuf);
424         generate(&key, t, DIGITS, &out, mech);
425         memset(key.mv_val, 0, key.mv_len);
426
427         /* compare */
428         if (out.mv_len != cred->bv_len) {
429                 rc = LUTIL_PASSWD_ERR;
430                 goto out;
431         }
432
433         rc = memcmp(out.mv_val, cred->bv_val, out.mv_len) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
434
435 out:
436         ber_memfree(key.mv_val);
437         return rc;
438 }
439
440 static int chk_totp1(
441         const struct berval *scheme,
442         const struct berval *passwd,
443         const struct berval *cred,
444         const char **text)
445 {
446         return chk_totp(passwd, cred, EVP_sha1(), text);
447 }
448
449 static int chk_totp256(
450         const struct berval *scheme,
451         const struct berval *passwd,
452         const struct berval *cred,
453         const char **text)
454 {
455         return chk_totp(passwd, cred, EVP_sha256(), text);
456 }
457
458 static int chk_totp512(
459         const struct berval *scheme,
460         const struct berval *passwd,
461         const struct berval *cred,
462         const char **text)
463 {
464         return chk_totp(passwd, cred, EVP_sha512(), text);
465 }
466
467 static int passwd_string32(
468         const struct berval *scheme,
469         const struct berval *passwd,
470         struct berval *hash)
471 {
472         int b32len = (passwd->bv_len + 4)/5 * 8;
473         int rc;
474         hash->bv_len = scheme->bv_len + b32len;
475         hash->bv_val = ber_memalloc(hash->bv_len + 1);
476         AC_MEMCPY(hash->bv_val, scheme->bv_val, scheme->bv_len);
477         rc = totp_b32_ntop((unsigned char *)passwd->bv_val, passwd->bv_len,
478                 hash->bv_val + scheme->bv_len, b32len+1);
479         if (rc < 0) {
480                 ber_memfree(hash->bv_val);
481                 hash->bv_val = NULL;
482                 return LUTIL_PASSWD_ERR;
483         }
484         return LUTIL_PASSWD_OK;
485 }
486
487 static int hash_totp1(
488         const struct berval *scheme,
489         const struct berval *passwd,
490         struct berval *hash,
491         const char **text)
492 {
493 #if 0
494         if (passwd->bv_len != SHA_DIGEST_LENGTH) {
495                 *text = "invalid key length";
496                 return LUTIL_PASSWD_ERR;
497         }
498 #endif
499         return passwd_string32(scheme, passwd, hash);
500 }
501
502 static int hash_totp256(
503         const struct berval *scheme,
504         const struct berval *passwd,
505         struct berval *hash,
506         const char **text)
507 {
508 #if 0
509         if (passwd->bv_len != SHA256_DIGEST_LENGTH) {
510                 *text = "invalid key length";
511                 return LUTIL_PASSWD_ERR;
512         }
513 #endif
514         return passwd_string32(scheme, passwd, hash);
515 }
516
517 static int hash_totp512(
518         const struct berval *scheme,
519         const struct berval *passwd,
520         struct berval *hash,
521         const char **text)
522 {
523 #if 0
524         if (passwd->bv_len != SHA512_DIGEST_LENGTH) {
525                 *text = "invalid key length";
526                 return LUTIL_PASSWD_ERR;
527         }
528 #endif
529         return passwd_string32(scheme, passwd, hash);
530 }
531
532 static int totp_op_cleanup(
533         Operation *op,
534         SlapReply *rs )
535 {
536         slap_callback *cb;
537
538         /* clear out the current key */
539         ldap_pvt_thread_pool_setkey( op->o_threadctx, totp_op_cleanup,
540                 NULL, 0, NULL, NULL );
541
542         /* free the callback */
543         cb = op->o_callback;
544         op->o_callback = cb->sc_next;
545         if (cb->sc_private)
546                 ber_bvfree_x(cb->sc_private, op->o_tmpmemctx);
547         op->o_tmpfree( cb, op->o_tmpmemctx );
548         return 0;
549 }
550
551 static int
552 totp_bind_response( Operation *op, SlapReply *rs )
553 {
554         Modifications *mod = NULL;
555         BackendInfo *bi = op->o_bd->bd_info;
556         Entry *e;
557         int rc;
558
559         /* we're only interested if the bind was successful */
560         if ( rs->sr_err != LDAP_SUCCESS )
561                 return SLAP_CB_CONTINUE;
562
563         rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
564         op->o_bd->bd_info = bi;
565
566         if ( rc != LDAP_SUCCESS ) {
567                 return SLAP_CB_CONTINUE;
568         }
569
570         {
571                 time_t now;
572                 Attribute *a;
573                 Modifications *m;
574                 char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
575                 struct berval timestamp;
576
577                 /* get the current time */
578                 now = op->o_time;
579
580                 /* update the authTimestamp in the user's entry with the current time */
581                 timestamp.bv_val = nowstr;
582                 timestamp.bv_len = sizeof(nowstr);
583                 slap_timestamp( &now, &timestamp );
584
585                 m = ch_calloc( sizeof(Modifications), 1 );
586                 m->sml_op = LDAP_MOD_REPLACE;
587                 m->sml_flags = 0;
588                 m->sml_type = ad_authTimestamp->ad_cname;
589                 m->sml_desc = ad_authTimestamp;
590                 m->sml_numvals = 1;
591                 m->sml_values = ch_calloc( sizeof(struct berval), 2 );
592                 m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
593
594                 ber_dupbv( &m->sml_values[0], &timestamp );
595                 ber_dupbv( &m->sml_nvalues[0], &timestamp );
596                 m->sml_next = mod;
597                 mod = m;
598
599                 /* get authTimestamp attribute, if it exists */
600                 if ((a = attr_find( e->e_attrs, ad_authTimestamp)) != NULL && op->o_callback->sc_private) {
601                         struct berval *bv = op->o_callback->sc_private;
602                         m = ch_calloc( sizeof(Modifications), 1 );
603                         m->sml_op = LDAP_MOD_DELETE;
604                         m->sml_flags = 0;
605                         m->sml_type = ad_authTimestamp->ad_cname;
606                         m->sml_desc = ad_authTimestamp;
607                         m->sml_numvals = 1;
608                         m->sml_values = ch_calloc( sizeof(struct berval), 2 );
609                         m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
610
611                         ber_dupbv( &m->sml_values[0], bv );
612                         ber_dupbv( &m->sml_nvalues[0], bv );
613                         m->sml_next = mod;
614                         mod = m;
615                 }
616         }
617
618 done:
619         be_entry_release_r( op, e );
620
621         /* perform the update */
622         if ( mod ) {
623                 Operation op2 = *op;
624                 SlapReply r2 = { REP_RESULT };
625                 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
626
627                 /* This is a DSA-specific opattr, it never gets replicated. */
628                 op2.o_tag = LDAP_REQ_MODIFY;
629                 op2.o_callback = &cb;
630                 op2.orm_modlist = mod;
631                 op2.o_dn = op->o_bd->be_rootdn;
632                 op2.o_ndn = op->o_bd->be_rootndn;
633                 op2.o_dont_replicate = 1;
634                 rc = op->o_bd->be_modify( &op2, &r2 );
635                 slap_mods_free( mod, 1 );
636                 if (rc != LDAP_SUCCESS) {
637                         /* slapd has logged this as a success already, but we
638                          * need to fail it because the authTimestamp changed
639                          * out from under us.
640                          */
641                         rs->sr_err = LDAP_INVALID_CREDENTIALS;
642                         connection2anonymous(op->o_conn);
643                         op2 = *op;
644                         op2.o_callback = NULL;
645                         send_ldap_result(&op2, rs);
646                         op->o_bd->bd_info = bi;
647                         return rs->sr_err;
648                 }
649         }
650
651         op->o_bd->bd_info = bi;
652         return SLAP_CB_CONTINUE;
653 }
654
655 static int totp_op_bind(
656         Operation *op,
657         SlapReply *rs )
658 {
659         /* If this is a simple Bind, stash the Op pointer so our chk
660          * function can find it. Set a cleanup callback to clear it
661          * out when the Bind completes.
662          */
663         if ( op->oq_bind.rb_method == LDAP_AUTH_SIMPLE ) {
664                 slap_callback *cb;
665                 ldap_pvt_thread_pool_setkey( op->o_threadctx,
666                         totp_op_cleanup, op, 0, NULL, NULL );
667                 cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
668                 cb->sc_response = totp_bind_response;
669                 cb->sc_cleanup = totp_op_cleanup;
670                 cb->sc_next = op->o_callback;
671                 op->o_callback = cb;
672         }
673         return SLAP_CB_CONTINUE;
674 }
675
676 static int totp_db_open(
677         BackendDB *be,
678         ConfigReply *cr
679 )
680 {
681         int rc = 0;
682
683         if (!ad_authTimestamp) {
684                 const char *text = NULL;
685                 rc = slap_str2ad("authTimestamp", &ad_authTimestamp, &text);
686                 if (rc) {
687                         rc = register_at(totp_OpSchema[0].def, totp_OpSchema[0].ad, 0 );
688                         if (rc) {
689                                 snprintf(cr->msg, sizeof(cr->msg), "unable to find or register authTimestamp attribute: %s (%d)",
690                                         text, rc);
691                                 Debug(LDAP_DEBUG_ANY, "totp: %s.\n", cr->msg, 0, 0);
692                         }
693                         ad_authTimestamp->ad_type->sat_flags |= SLAP_AT_MANAGEABLE;
694                 }
695         }
696         return rc;
697 }
698
699 static slap_overinst totp;
700
701 int
702 totp_initialize(void)
703 {
704         int rc;
705
706         totp.on_bi.bi_type = "totp";
707
708         totp.on_bi.bi_db_open = totp_db_open;
709         totp.on_bi.bi_op_bind = totp_op_bind;
710
711         rc = lutil_passwd_add((struct berval *) &scheme_totp1, chk_totp1, hash_totp1);
712         if (!rc)
713                 rc = lutil_passwd_add((struct berval *) &scheme_totp256, chk_totp256, hash_totp256);
714         if (!rc)
715                 rc = lutil_passwd_add((struct berval *) &scheme_totp512, chk_totp512, hash_totp512);
716         if (rc)
717                 return rc;
718
719         return overlay_register(&totp);
720 }
721
722 int init_module(int argc, char *argv[]) {
723         return totp_initialize();
724 }