1e18a033bSKonstantin Ananyev
2e18a033bSKonstantin Ananyev/*
3e18a033bSKonstantin Ananyev * Copyright (C) Maxim Dounin
4e18a033bSKonstantin Ananyev */
5e18a033bSKonstantin Ananyev
6e18a033bSKonstantin Ananyev
7e18a033bSKonstantin Ananyev#include <ngx_config.h>
8e18a033bSKonstantin Ananyev#include <ngx_core.h>
9e18a033bSKonstantin Ananyev#include <ngx_crypt.h>
10e18a033bSKonstantin Ananyev#include <ngx_md5.h>
11e18a033bSKonstantin Ananyev#include <ngx_sha1.h>
12e18a033bSKonstantin Ananyev
13e18a033bSKonstantin Ananyev
14e18a033bSKonstantin Ananyev#if (NGX_CRYPT)
15e18a033bSKonstantin Ananyev
16e18a033bSKonstantin Ananyevstatic ngx_int_t ngx_crypt_apr1(ngx_pool_t *pool, u_char *key, u_char *salt,
17e18a033bSKonstantin Ananyev    u_char **encrypted);
18e18a033bSKonstantin Ananyevstatic ngx_int_t ngx_crypt_plain(ngx_pool_t *pool, u_char *key, u_char *salt,
19e18a033bSKonstantin Ananyev    u_char **encrypted);
20e18a033bSKonstantin Ananyevstatic ngx_int_t ngx_crypt_ssha(ngx_pool_t *pool, u_char *key, u_char *salt,
21e18a033bSKonstantin Ananyev    u_char **encrypted);
22e18a033bSKonstantin Ananyevstatic ngx_int_t ngx_crypt_sha(ngx_pool_t *pool, u_char *key, u_char *salt,
23e18a033bSKonstantin Ananyev    u_char **encrypted);
24e18a033bSKonstantin Ananyev
25e18a033bSKonstantin Ananyev
26e18a033bSKonstantin Ananyevstatic u_char *ngx_crypt_to64(u_char *p, uint32_t v, size_t n);
27e18a033bSKonstantin Ananyev
28e18a033bSKonstantin Ananyev
29e18a033bSKonstantin Ananyevngx_int_t
30e18a033bSKonstantin Ananyevngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
31e18a033bSKonstantin Ananyev{
32e18a033bSKonstantin Ananyev    if (ngx_strncmp(salt, "$apr1$", sizeof("$apr1$") - 1) == 0) {
33e18a033bSKonstantin Ananyev        return ngx_crypt_apr1(pool, key, salt, encrypted);
34e18a033bSKonstantin Ananyev
35e18a033bSKonstantin Ananyev    } else if (ngx_strncmp(salt, "{PLAIN}", sizeof("{PLAIN}") - 1) == 0) {
36e18a033bSKonstantin Ananyev        return ngx_crypt_plain(pool, key, salt, encrypted);
37e18a033bSKonstantin Ananyev
38e18a033bSKonstantin Ananyev    } else if (ngx_strncmp(salt, "{SSHA}", sizeof("{SSHA}") - 1) == 0) {
39e18a033bSKonstantin Ananyev        return ngx_crypt_ssha(pool, key, salt, encrypted);
40e18a033bSKonstantin Ananyev
41e18a033bSKonstantin Ananyev    } else if (ngx_strncmp(salt, "{SHA}", sizeof("{SHA}") - 1) == 0) {
42e18a033bSKonstantin Ananyev        return ngx_crypt_sha(pool, key, salt, encrypted);
43e18a033bSKonstantin Ananyev    }
44e18a033bSKonstantin Ananyev
45e18a033bSKonstantin Ananyev    /* fallback to libc crypt() */
46e18a033bSKonstantin Ananyev
47e18a033bSKonstantin Ananyev    return ngx_libc_crypt(pool, key, salt, encrypted);
48e18a033bSKonstantin Ananyev}
49e18a033bSKonstantin Ananyev
50e18a033bSKonstantin Ananyev
51e18a033bSKonstantin Ananyevstatic ngx_int_t
52e18a033bSKonstantin Ananyevngx_crypt_apr1(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
53e18a033bSKonstantin Ananyev{
54e18a033bSKonstantin Ananyev    ngx_int_t          n;
55e18a033bSKonstantin Ananyev    ngx_uint_t         i;
56e18a033bSKonstantin Ananyev    u_char            *p, *last, final[16];
57e18a033bSKonstantin Ananyev    size_t             saltlen, keylen;
58e18a033bSKonstantin Ananyev    ngx_md5_t          md5, ctx1;
59e18a033bSKonstantin Ananyev
60e18a033bSKonstantin Ananyev    /* Apache's apr1 crypt is Poul-Henning Kamp's md5 crypt with $apr1$ magic */
61e18a033bSKonstantin Ananyev
62e18a033bSKonstantin Ananyev    keylen = ngx_strlen(key);
63e18a033bSKonstantin Ananyev
64e18a033bSKonstantin Ananyev    /* true salt: no magic, max 8 chars, stop at first $ */
65e18a033bSKonstantin Ananyev
66e18a033bSKonstantin Ananyev    salt += sizeof("$apr1$") - 1;
67e18a033bSKonstantin Ananyev    last = salt + 8;
68e18a033bSKonstantin Ananyev    for (p = salt; *p && *p != '$' && p < last; p++) { /* void */ }
69e18a033bSKonstantin Ananyev    saltlen = p - salt;
70e18a033bSKonstantin Ananyev
71e18a033bSKonstantin Ananyev    /* hash key and salt */
72e18a033bSKonstantin Ananyev
73e18a033bSKonstantin Ananyev    ngx_md5_init(&md5);
74e18a033bSKonstantin Ananyev    ngx_md5_update(&md5, key, keylen);
75e18a033bSKonstantin Ananyev    ngx_md5_update(&md5, (u_char *) "$apr1$", sizeof("$apr1$") - 1);
76e18a033bSKonstantin Ananyev    ngx_md5_update(&md5, salt, saltlen);
77e18a033bSKonstantin Ananyev
78e18a033bSKonstantin Ananyev    ngx_md5_init(&ctx1);
79e18a033bSKonstantin Ananyev    ngx_md5_update(&ctx1, key, keylen);
80e18a033bSKonstantin Ananyev    ngx_md5_update(&ctx1, salt, saltlen);
81e18a033bSKonstantin Ananyev    ngx_md5_update(&ctx1, key, keylen);
82e18a033bSKonstantin Ananyev    ngx_md5_final(final, &ctx1);
83e18a033bSKonstantin Ananyev
84e18a033bSKonstantin Ananyev    for (n = keylen; n > 0; n -= 16) {
85e18a033bSKonstantin Ananyev        ngx_md5_update(&md5, final, n > 16 ? 16 : n);
86e18a033bSKonstantin Ananyev    }
87e18a033bSKonstantin Ananyev
88e18a033bSKonstantin Ananyev    ngx_memzero(final, sizeof(final));
89e18a033bSKonstantin Ananyev
90e18a033bSKonstantin Ananyev    for (i = keylen; i; i >>= 1) {
91e18a033bSKonstantin Ananyev        if (i & 1) {
92e18a033bSKonstantin Ananyev            ngx_md5_update(&md5, final, 1);
93e18a033bSKonstantin Ananyev
94e18a033bSKonstantin Ananyev        } else {
95e18a033bSKonstantin Ananyev            ngx_md5_update(&md5, key, 1);
96e18a033bSKonstantin Ananyev        }
97e18a033bSKonstantin Ananyev    }
98e18a033bSKonstantin Ananyev
99e18a033bSKonstantin Ananyev    ngx_md5_final(final, &md5);
100e18a033bSKonstantin Ananyev
101e18a033bSKonstantin Ananyev    for (i = 0; i < 1000; i++) {
102e18a033bSKonstantin Ananyev        ngx_md5_init(&ctx1);
103e18a033bSKonstantin Ananyev
104e18a033bSKonstantin Ananyev        if (i & 1) {
105e18a033bSKonstantin Ananyev            ngx_md5_update(&ctx1, key, keylen);
106e18a033bSKonstantin Ananyev
107e18a033bSKonstantin Ananyev        } else {
108e18a033bSKonstantin Ananyev            ngx_md5_update(&ctx1, final, 16);
109e18a033bSKonstantin Ananyev        }
110e18a033bSKonstantin Ananyev
111e18a033bSKonstantin Ananyev        if (i % 3) {
112e18a033bSKonstantin Ananyev            ngx_md5_update(&ctx1, salt, saltlen);
113e18a033bSKonstantin Ananyev        }
114e18a033bSKonstantin Ananyev
115e18a033bSKonstantin Ananyev        if (i % 7) {
116e18a033bSKonstantin Ananyev            ngx_md5_update(&ctx1, key, keylen);
117e18a033bSKonstantin Ananyev        }
118e18a033bSKonstantin Ananyev
119e18a033bSKonstantin Ananyev        if (i & 1) {
120e18a033bSKonstantin Ananyev            ngx_md5_update(&ctx1, final, 16);
121e18a033bSKonstantin Ananyev
122e18a033bSKonstantin Ananyev        } else {
123e18a033bSKonstantin Ananyev            ngx_md5_update(&ctx1, key, keylen);
124e18a033bSKonstantin Ananyev        }
125e18a033bSKonstantin Ananyev
126e18a033bSKonstantin Ananyev        ngx_md5_final(final, &ctx1);
127e18a033bSKonstantin Ananyev    }
128e18a033bSKonstantin Ananyev
129e18a033bSKonstantin Ananyev    /* output */
130e18a033bSKonstantin Ananyev
131e18a033bSKonstantin Ananyev    *encrypted = ngx_pnalloc(pool, sizeof("$apr1$") - 1 + saltlen + 1 + 22 + 1);
132e18a033bSKonstantin Ananyev    if (*encrypted == NULL) {
133e18a033bSKonstantin Ananyev        return NGX_ERROR;
134e18a033bSKonstantin Ananyev    }
135e18a033bSKonstantin Ananyev
136e18a033bSKonstantin Ananyev    p = ngx_cpymem(*encrypted, "$apr1$", sizeof("$apr1$") - 1);
137e18a033bSKonstantin Ananyev    p = ngx_copy(p, salt, saltlen);
138e18a033bSKonstantin Ananyev    *p++ = '$';
139e18a033bSKonstantin Ananyev
140e18a033bSKonstantin Ananyev    p = ngx_crypt_to64(p, (final[ 0]<<16) | (final[ 6]<<8) | final[12], 4);
141e18a033bSKonstantin Ananyev    p = ngx_crypt_to64(p, (final[ 1]<<16) | (final[ 7]<<8) | final[13], 4);
142e18a033bSKonstantin Ananyev    p = ngx_crypt_to64(p, (final[ 2]<<16) | (final[ 8]<<8) | final[14], 4);
143e18a033bSKonstantin Ananyev    p = ngx_crypt_to64(p, (final[ 3]<<16) | (final[ 9]<<8) | final[15], 4);
144e18a033bSKonstantin Ananyev    p = ngx_crypt_to64(p, (final[ 4]<<16) | (final[10]<<8) | final[ 5], 4);
145e18a033bSKonstantin Ananyev    p = ngx_crypt_to64(p, final[11], 2);
146e18a033bSKonstantin Ananyev    *p = '\0';
147e18a033bSKonstantin Ananyev
148e18a033bSKonstantin Ananyev    return NGX_OK;
149e18a033bSKonstantin Ananyev}
150e18a033bSKonstantin Ananyev
151e18a033bSKonstantin Ananyev
152e18a033bSKonstantin Ananyevstatic u_char *
153e18a033bSKonstantin Ananyevngx_crypt_to64(u_char *p, uint32_t v, size_t n)
154e18a033bSKonstantin Ananyev{
155e18a033bSKonstantin Ananyev    static u_char   itoa64[] =
156e18a033bSKonstantin Ananyev        "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
157e18a033bSKonstantin Ananyev
158e18a033bSKonstantin Ananyev    while (n--) {
159e18a033bSKonstantin Ananyev        *p++ = itoa64[v & 0x3f];
160e18a033bSKonstantin Ananyev        v >>= 6;
161e18a033bSKonstantin Ananyev    }
162e18a033bSKonstantin Ananyev
163e18a033bSKonstantin Ananyev    return p;
164e18a033bSKonstantin Ananyev}
165e18a033bSKonstantin Ananyev
166e18a033bSKonstantin Ananyev
167e18a033bSKonstantin Ananyevstatic ngx_int_t
168e18a033bSKonstantin Ananyevngx_crypt_plain(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
169e18a033bSKonstantin Ananyev{
170e18a033bSKonstantin Ananyev    size_t   len;
171e18a033bSKonstantin Ananyev    u_char  *p;
172e18a033bSKonstantin Ananyev
173e18a033bSKonstantin Ananyev    len = ngx_strlen(key);
174e18a033bSKonstantin Ananyev
175e18a033bSKonstantin Ananyev    *encrypted = ngx_pnalloc(pool, sizeof("{PLAIN}") - 1 + len + 1);
176e18a033bSKonstantin Ananyev    if (*encrypted == NULL) {
177e18a033bSKonstantin Ananyev        return NGX_ERROR;
178e18a033bSKonstantin Ananyev    }
179e18a033bSKonstantin Ananyev
180e18a033bSKonstantin Ananyev    p = ngx_cpymem(*encrypted, "{PLAIN}", sizeof("{PLAIN}") - 1);
181e18a033bSKonstantin Ananyev    ngx_memcpy(p, key, len + 1);
182e18a033bSKonstantin Ananyev
183e18a033bSKonstantin Ananyev    return NGX_OK;
184e18a033bSKonstantin Ananyev}
185e18a033bSKonstantin Ananyev
186e18a033bSKonstantin Ananyev
187e18a033bSKonstantin Ananyevstatic ngx_int_t
188e18a033bSKonstantin Ananyevngx_crypt_ssha(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
189e18a033bSKonstantin Ananyev{
190e18a033bSKonstantin Ananyev    size_t       len;
191e18a033bSKonstantin Ananyev    ngx_int_t    rc;
192e18a033bSKonstantin Ananyev    ngx_str_t    encoded, decoded;
193e18a033bSKonstantin Ananyev    ngx_sha1_t   sha1;
194e18a033bSKonstantin Ananyev
195e18a033bSKonstantin Ananyev    /* "{SSHA}" base64(SHA1(key salt) salt) */
196e18a033bSKonstantin Ananyev
197e18a033bSKonstantin Ananyev    /* decode base64 salt to find out true salt */
198e18a033bSKonstantin Ananyev
199e18a033bSKonstantin Ananyev    encoded.data = salt + sizeof("{SSHA}") - 1;
200e18a033bSKonstantin Ananyev    encoded.len = ngx_strlen(encoded.data);
201e18a033bSKonstantin Ananyev
202e18a033bSKonstantin Ananyev    len = ngx_max(ngx_base64_decoded_length(encoded.len), 20);
203e18a033bSKonstantin Ananyev
204e18a033bSKonstantin Ananyev    decoded.data = ngx_pnalloc(pool, len);
205e18a033bSKonstantin Ananyev    if (decoded.data == NULL) {
206e18a033bSKonstantin Ananyev        return NGX_ERROR;
207e18a033bSKonstantin Ananyev    }
208e18a033bSKonstantin Ananyev
209e18a033bSKonstantin Ananyev    rc = ngx_decode_base64(&decoded, &encoded);
210e18a033bSKonstantin Ananyev
211e18a033bSKonstantin Ananyev    if (rc != NGX_OK || decoded.len < 20) {
212e18a033bSKonstantin Ananyev        decoded.len = 20;
213e18a033bSKonstantin Ananyev    }
214e18a033bSKonstantin Ananyev
215e18a033bSKonstantin Ananyev    /* update SHA1 from key and salt */
216e18a033bSKonstantin Ananyev
217e18a033bSKonstantin Ananyev    ngx_sha1_init(&sha1);
218e18a033bSKonstantin Ananyev    ngx_sha1_update(&sha1, key, ngx_strlen(key));
219e18a033bSKonstantin Ananyev    ngx_sha1_update(&sha1, decoded.data + 20, decoded.len - 20);
220e18a033bSKonstantin Ananyev    ngx_sha1_final(decoded.data, &sha1);
221e18a033bSKonstantin Ananyev
222e18a033bSKonstantin Ananyev    /* encode it back to base64 */
223e18a033bSKonstantin Ananyev
224e18a033bSKonstantin Ananyev    len = sizeof("{SSHA}") - 1 + ngx_base64_encoded_length(decoded.len) + 1;
225e18a033bSKonstantin Ananyev
226e18a033bSKonstantin Ananyev    *encrypted = ngx_pnalloc(pool, len);
227e18a033bSKonstantin Ananyev    if (*encrypted == NULL) {
228e18a033bSKonstantin Ananyev        return NGX_ERROR;
229e18a033bSKonstantin Ananyev    }
230e18a033bSKonstantin Ananyev
231e18a033bSKonstantin Ananyev    encoded.data = ngx_cpymem(*encrypted, "{SSHA}", sizeof("{SSHA}") - 1);
232e18a033bSKonstantin Ananyev    ngx_encode_base64(&encoded, &decoded);
233e18a033bSKonstantin Ananyev    encoded.data[encoded.len] = '\0';
234e18a033bSKonstantin Ananyev
235e18a033bSKonstantin Ananyev    return NGX_OK;
236e18a033bSKonstantin Ananyev}
237e18a033bSKonstantin Ananyev
238e18a033bSKonstantin Ananyev
239e18a033bSKonstantin Ananyevstatic ngx_int_t
240e18a033bSKonstantin Ananyevngx_crypt_sha(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
241e18a033bSKonstantin Ananyev{
242e18a033bSKonstantin Ananyev    size_t      len;
243e18a033bSKonstantin Ananyev    ngx_str_t   encoded, decoded;
244e18a033bSKonstantin Ananyev    ngx_sha1_t  sha1;
245e18a033bSKonstantin Ananyev    u_char      digest[20];
246e18a033bSKonstantin Ananyev
247e18a033bSKonstantin Ananyev    /* "{SHA}" base64(SHA1(key)) */
248e18a033bSKonstantin Ananyev
249e18a033bSKonstantin Ananyev    decoded.len = sizeof(digest);
250e18a033bSKonstantin Ananyev    decoded.data = digest;
251e18a033bSKonstantin Ananyev
252e18a033bSKonstantin Ananyev    ngx_sha1_init(&sha1);
253e18a033bSKonstantin Ananyev    ngx_sha1_update(&sha1, key, ngx_strlen(key));
254e18a033bSKonstantin Ananyev    ngx_sha1_final(digest, &sha1);
255e18a033bSKonstantin Ananyev
256e18a033bSKonstantin Ananyev    len = sizeof("{SHA}") - 1 + ngx_base64_encoded_length(decoded.len) + 1;
257e18a033bSKonstantin Ananyev
258e18a033bSKonstantin Ananyev    *encrypted = ngx_pnalloc(pool, len);
259e18a033bSKonstantin Ananyev    if (*encrypted == NULL) {
260e18a033bSKonstantin Ananyev        return NGX_ERROR;
261e18a033bSKonstantin Ananyev    }
262e18a033bSKonstantin Ananyev
263e18a033bSKonstantin Ananyev    encoded.data = ngx_cpymem(*encrypted, "{SHA}", sizeof("{SHA}") - 1);
264e18a033bSKonstantin Ananyev    ngx_encode_base64(&encoded, &decoded);
265e18a033bSKonstantin Ananyev    encoded.data[encoded.len] = '\0';
266e18a033bSKonstantin Ananyev
267e18a033bSKonstantin Ananyev    return NGX_OK;
268e18a033bSKonstantin Ananyev}
269e18a033bSKonstantin Ananyev
270e18a033bSKonstantin Ananyev#endif /* NGX_CRYPT */
271