21cc7d05ef0f01bbeb3b46498eee7a96a83ba3f4
[radsecproxy.git] / lib / radius / crypto.c
1 /*
2 Copyright (c) 2011, Network RADIUS SARL
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7     * Redistributions of source code must retain the above copyright
8       notice, this list of conditions and the following disclaimer.
9     * Redistributions in binary form must reproduce the above copyright
10       notice, this list of conditions and the following disclaimer in the
11       documentation and/or other materials provided with the distribution.
12     * Neither the name of the <organization> nor the
13       names of its contributors may be used to endorse or promote products
14       derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 /** \file crypto.c
29  *  \brief Data obfuscation and signing, using MD5.
30  *
31  *  The "encryption" methods defined here are export-safe.  The
32  *  technical cryptography name for these functions is "obfuscation".
33  *  They cannot properly be called "encryption", in the same way that
34  *  DES or AES performs encryption.
35  */
36
37 /** \cond PRIVATE */
38
39 #include        "client.h"
40
41
42 ssize_t nr_password_encrypt(uint8_t *output, size_t outlen,
43                            const uint8_t *input, size_t inlen,
44                            const char *secret, const uint8_t *vector)
45 {
46         size_t i, j, len;
47         uint8_t digest[16];
48         RS_MD5_CTX ctx, secret_ctx;
49
50         if (!output || (outlen < 16) || !input || (inlen == 0) ||
51             !secret || !vector) {
52                 return -RSE_INVAL;
53         }
54
55         len = inlen;
56         if (len > 128) return -RSE_ATTR_OVERFLOW;
57
58         len = (len + 0x0f) & ~0x0f; /* round up to 16 byte boundary */
59
60         if (outlen < len) return -RSE_ATTR_OVERFLOW;
61
62         memcpy(output, input, len);
63         memset(output + len, 0, 128 - len);
64
65         RS_MD5Init(&secret_ctx);
66         RS_MD5Update(&secret_ctx, (const uint8_t *) secret, strlen(secret));
67
68         for (j = 0; j < len; j += 16) {
69                 ctx = secret_ctx;
70
71                 if (j == 0) {
72                         RS_MD5Update(&ctx, vector, 16);
73                         RS_MD5Final(digest, &ctx);
74                 } else {
75                         RS_MD5Update(&ctx, &output[j - 16], 16);
76                         RS_MD5Final(digest, &ctx);
77                 }
78
79                 for (i = 0; i < 16; i++) {
80                         output[i + j] ^= digest[i];
81                 }
82         }
83
84         return len;
85 }
86
87 #ifdef FLAG_ENCRYPT_TUNNEL_PASSWORD
88 ssize_t nr_tunnelpw_encrypt(uint8_t *output, size_t outlen,
89                             const uint8_t *input, size_t inlen,
90                             const char *secret, const uint8_t *vector)
91 {
92         size_t i, j, len;
93         RS_MD5_CTX ctx, secret_ctx;
94         uint8_t digest[16];
95
96         if (!output || (outlen < 18) || !input || (inlen == 0) ||
97             !secret || !vector) {
98                 return -RSE_INVAL;
99         }
100
101         len = ((inlen + 1) + 0x0f) & ~0x0f;
102         if (len > 251) return -RSE_ATTR_OVERFLOW;
103
104         output[0] = (nr_rand() & 0xff) | 0x80;
105         output[1] = nr_rand() & 0xff;
106         output[2] = inlen;
107
108         memcpy(output + 3, input, inlen);
109         memset(output + 3 + inlen, 0, len - inlen - 1);
110
111         RS_MD5Init(&secret_ctx);
112         RS_MD5Update(&secret_ctx, (const uint8_t *) secret, strlen(secret));
113
114         for (j = 0; j < len; j += 16) {
115                 ctx = secret_ctx;
116
117                 if (j == 0) {
118                         RS_MD5Update(&ctx, vector, 16);
119                         RS_MD5Update(&ctx, output, 2);
120                         RS_MD5Final(digest, &ctx);
121                 } else {
122                         RS_MD5Update(&ctx, &output[j + 2 - 16], 16);
123                         RS_MD5Final(digest, &ctx);
124                 }
125
126                 for (i = 0; i < 16; i++) {
127                         output[i + j + 2] ^= digest[i];
128                 }
129         }
130
131         return len + 2;
132 }
133
134 ssize_t nr_tunnelpw_decrypt(uint8_t *output, size_t outlen,
135                             const uint8_t *input, size_t inlen,
136                             const char *secret, const uint8_t *vector)
137 {
138         size_t i, j, len, encoded_len;
139         RS_MD5_CTX ctx, secret_ctx;
140         uint8_t digest[16];
141
142         if (!output || (outlen < 1) || !input || (inlen < 2) ||
143             !secret || !vector) {
144                 return -RSE_INVAL;
145         }
146
147         if (inlen <= 3) {
148                 output[0] = 0;
149                 return 0;
150         }
151
152         len = inlen - 2;
153
154         if (outlen < (len - 1)) return -RSE_ATTR_OVERFLOW;
155
156         RS_MD5Init(&secret_ctx);
157         RS_MD5Update(&secret_ctx, (const uint8_t *) secret, strlen(secret));
158
159         ctx = secret_ctx;
160
161         RS_MD5Update(&ctx, vector, 16); /* MD5(secret + vector + salt) */
162         RS_MD5Update(&ctx, input, 2);
163         RS_MD5Final(digest, &ctx);
164
165         encoded_len = input[2] ^ digest[0];
166         if (encoded_len >= len) {
167                 return -RSE_ATTR_TOO_LARGE;
168         }
169
170         for (i = 0; i < 15; i++) {
171                 output[i] = input[i + 3] ^ digest[i + 1];
172         }
173
174         for (j = 16; j < len; j += 16) {
175                 ctx = secret_ctx;
176
177                 RS_MD5Update(&ctx, input + j - 16 + 2, 16);
178                 RS_MD5Final(digest, &ctx);
179
180                 for (i = 0; i < 16; i++) {
181                         output[i + j - 1] = input[i + j + 2] ^ digest[i];
182                 }
183                 
184
185         }
186
187         output[encoded_len] = '\0';
188         return encoded_len;
189 }
190 #endif
191
192 void
193 nr_hmac_md5(const uint8_t *data, size_t data_len,
194             const uint8_t *key, size_t key_len,
195             uint8_t digest[16])
196 {
197         size_t i;
198         uint8_t k_ipad[64];
199         uint8_t k_opad[64];
200         uint8_t tk[16];
201         RS_MD5_CTX ctx;
202
203         if (key_len > 64) {
204                 RS_MD5Init(&ctx);
205                 RS_MD5Update(&ctx, key, key_len);
206                 RS_MD5Final(tk, &ctx);
207
208                 key = tk;
209                 key_len = 16;
210         }
211
212         memset(k_ipad, 0, sizeof(k_ipad));
213         memset(k_opad, 0, sizeof(k_opad));
214         memcpy(k_ipad, key, key_len);
215         memcpy(k_opad, key, key_len);
216
217         for (i = 0; i < sizeof(k_ipad); i++) {
218                 k_ipad[i] ^= 0x36;
219                 k_opad[i] ^= 0x5c;
220         }
221
222         RS_MD5Init(&ctx); 
223         RS_MD5Update(&ctx, k_ipad, sizeof(k_ipad));
224         RS_MD5Update(&ctx, data, data_len);
225         RS_MD5Final(digest, &ctx);
226
227         RS_MD5Init(&ctx);
228         RS_MD5Update(&ctx, k_opad, sizeof(k_opad));
229         RS_MD5Update(&ctx, digest, 16);
230         RS_MD5Final(digest, &ctx);
231 }
232
233 /** \endcond */