1 /*
2 * ProFTPD - mod_auth_otp
3 * Copyright (c) 2015 TJ Saunders
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 *
19 * As a special exemption, TJ Saunders and other respective copyright holders
20 * give permission to link this program with OpenSSL, and distribute the
21 * resulting executable, without including the source code for OpenSSL in the
22 * source distribution.
23 */
24
25 #include "otp.h"
26 #include "crypto.h"
27
28 static const char *trace_channel = "auth_otp";
29
otp(pool * p,const EVP_MD * md,const unsigned char * key,size_t key_len,unsigned long counter,unsigned int * code)30 static int otp(pool *p, const EVP_MD *md,
31 const unsigned char *key, size_t key_len,
32 unsigned long counter, unsigned int *code) {
33 register unsigned int i;
34 unsigned char hash[EVP_MAX_MD_SIZE], value[8];
35 size_t hash_len;
36 int offset = 0;
37 unsigned int truncated = 0;
38
39 /* RFC 4226 requires a big-endian ordering of the counter value. While
40 * arranging that, encode the counter value into an unsigned char buffer
41 * for feeding into the HMAC function.
42 */
43 for (i = sizeof(value); i--; counter >>= 8) {
44 value[i] = counter;
45 }
46
47 hash_len = EVP_MAX_MD_SIZE;
48 if (auth_otp_hmac(md, key, key_len, value, sizeof(value), hash,
49 &hash_len) < 0) {
50 return -1;
51 }
52
53 pr_memscrub(value, sizeof(value));
54
55 offset = hash[hash_len-1] & 0x0f;
56
57 truncated = ((hash[offset+0] & 0x7f) << 24) |
58 ((hash[offset+1] & 0xff) << 16) |
59 ((hash[offset+2] & 0xff) << 8) |
60 (hash[offset+3] & 0xff);
61
62 pr_memscrub(hash, sizeof(hash));
63
64 truncated &= 0x7fffffff;
65
66 /* Note the 6 zeroes here; this determines the number of digits in the
67 * generated code.
68 */
69 *code = truncated % 1000000;
70 return 0;
71 }
72
auth_otp_hotp(pool * p,const unsigned char * key,size_t key_len,unsigned long counter,unsigned int * code)73 int auth_otp_hotp(pool *p, const unsigned char *key, size_t key_len,
74 unsigned long counter, unsigned int *code) {
75 const EVP_MD *md;
76 int res;
77
78 if (p == NULL ||
79 key == NULL ||
80 key_len == 0 ||
81 code == NULL) {
82 errno = EINVAL;
83 return -1;
84 }
85
86 /* RFC 4226 (HOTP) uses HMAC-SHA1. */
87 md = EVP_sha1();
88
89 res = otp(p, md, key, key_len, counter, code);
90 return res;
91 }
92
auth_otp_totp(pool * p,const unsigned char * key,size_t key_len,unsigned long ts,unsigned int algo,unsigned int * code)93 int auth_otp_totp(pool *p, const unsigned char *key, size_t key_len,
94 unsigned long ts, unsigned int algo, unsigned int *code) {
95 const EVP_MD *md;
96 unsigned long counter;
97 int res;
98
99 if (p == NULL ||
100 key == NULL ||
101 key_len == 0 ||
102 code == NULL) {
103 errno = EINVAL;
104 return -1;
105 }
106
107 switch (algo) {
108 case AUTH_OTP_ALGO_TOTP_SHA1:
109 md = EVP_sha1();
110 break;
111
112 #ifdef HAVE_SHA256_OPENSSL
113 case AUTH_OTP_ALGO_TOTP_SHA256:
114 md = EVP_sha256();
115 break;
116 #endif /* SHA256 OpenSSL support */
117
118 #ifdef HAVE_SHA512_OPENSSL
119 case AUTH_OTP_ALGO_TOTP_SHA512:
120 md = EVP_sha512();
121 break;
122 #endif /* SHA512 OpenSSL support */
123
124 default:
125 pr_trace_msg(trace_channel, 4,
126 "unsupported TOTP algorithm ID %u requested", algo);
127 errno = EINVAL;
128 return -1;
129 }
130
131 counter = ts / AUTH_OTP_TOTP_TIMESTEP_SECS;
132 res = otp(p, md, key, key_len, counter, code);
133 return res;
134 }
135