xref: /haiku/src/system/libroot/posix/crypt/crypto_scrypt.cpp (revision 4d8811742fa447ec05b4993a16a0931bc29aafab)
1 /*-
2  * Copyright 2009 Colin Percival
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
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * This file was originally written by Colin Percival as part of the Tarsnap
27  * online backup system.
28  */
29 #include <sys/types.h>
30 #include <sys/mman.h>
31 
32 #include <errno.h>
33 #include <stdint.h>
34 #include <stdlib.h>
35 #include <string.h>
36 
37 #include "pbkdf2.h"
38 
39 #include "crypto_scrypt_smix.h"
40 
41 #include "crypto_scrypt.h"
42 
43 static void (*smix_func)(uint8_t *, size_t, uint64_t, void *, void *) = NULL;
44 
45 /**
46  * _crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen, smix):
47  * Perform the requested scrypt computation, using ${smix} as the smix routine.
48  */
49 static int
50 _crypto_scrypt(const uint8_t * passwd, size_t passwdlen,
51     const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t _r, uint32_t _p,
52     uint8_t * buf, size_t buflen,
53     void (*smix)(uint8_t *, size_t, uint64_t, void *, void *))
54 {
55 	void * B0, * V0, * XY0;
56 	uint8_t * B;
57 	uint32_t * V;
58 	uint32_t * XY;
59 	size_t r = _r, p = _p;
60 	uint32_t i;
61 
62 	/* Sanity-check parameters. */
63 #if SIZE_MAX > UINT32_MAX
64 	if (buflen > (((uint64_t)(1) << 32) - 1) * 32) {
65 		errno = EFBIG;
66 		goto err0;
67 	}
68 #endif
69 	if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) {
70 		errno = EFBIG;
71 		goto err0;
72 	}
73 	if (((N & (N - 1)) != 0) || (N < 2)) {
74 		errno = EINVAL;
75 		goto err0;
76 	}
77 	if ((r > SIZE_MAX / 128 / p) ||
78 #if SIZE_MAX / 256 <= UINT32_MAX
79 	    (r > (SIZE_MAX - 64) / 256) ||
80 #endif
81 	    (N > SIZE_MAX / 128 / r)) {
82 		errno = ENOMEM;
83 		goto err0;
84 	}
85 
86 	/* Allocate memory. */
87 #ifdef HAVE_POSIX_MEMALIGN
88 	if ((errno = posix_memalign(&B0, 64, 128 * r * p)) != 0)
89 		goto err0;
90 	B = (uint8_t *)(B0);
91 	if ((errno = posix_memalign(&XY0, 64, 256 * r + 64)) != 0)
92 		goto err1;
93 	XY = (uint32_t *)(XY0);
94 #if !defined(MAP_ANON) || !defined(HAVE_MMAP)
95 	if ((errno = posix_memalign(&V0, 64, 128 * r * N)) != 0)
96 		goto err2;
97 	V = (uint32_t *)(V0);
98 #endif
99 #else
100 	if ((B0 = malloc(128 * r * p + 63)) == NULL)
101 		goto err0;
102 	B = (uint8_t *)(((uintptr_t)(B0) + 63) & ~ (uintptr_t)(63));
103 	if ((XY0 = malloc(256 * r + 64 + 63)) == NULL)
104 		goto err1;
105 	XY = (uint32_t *)(((uintptr_t)(XY0) + 63) & ~ (uintptr_t)(63));
106 #if !defined(MAP_ANON) || !defined(HAVE_MMAP)
107 	if ((V0 = malloc(128 * r * N + 63)) == NULL)
108 		goto err2;
109 	V = (uint32_t *)(((uintptr_t)(V0) + 63) & ~ (uintptr_t)(63));
110 #endif
111 #endif
112 #if defined(MAP_ANON) && defined(HAVE_MMAP)
113 	if ((V0 = mmap(NULL, 128 * r * N, PROT_READ | PROT_WRITE,
114 #ifdef MAP_NOCORE
115 	    MAP_ANON | MAP_PRIVATE | MAP_NOCORE,
116 #else
117 	    MAP_ANON | MAP_PRIVATE,
118 #endif
119 	    -1, 0)) == MAP_FAILED)
120 		goto err2;
121 	V = (uint32_t *)(V0);
122 #endif
123 
124 	/* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */
125 	PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r);
126 
127 	/* 2: for i = 0 to p - 1 do */
128 	for (i = 0; i < p; i++) {
129 		/* 3: B_i <-- MF(B_i, N) */
130 		(smix)(&B[i * 128 * r], r, N, V, XY);
131 	}
132 
133 	/* 5: DK <-- PBKDF2(P, B, 1, dkLen) */
134 	PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen);
135 
136 	/* Free memory. */
137 #if defined(MAP_ANON) && defined(HAVE_MMAP)
138 	if (munmap(V0, 128 * r * N))
139 		goto err2;
140 #else
141 	free(V0);
142 #endif
143 	free(XY0);
144 	free(B0);
145 
146 	/* Success! */
147 	return (0);
148 
149 err2:
150 	free(XY0);
151 err1:
152 	free(B0);
153 err0:
154 	/* Failure! */
155 	return (-1);
156 }
157 
158 #define TESTLEN 64
159 static struct scrypt_test {
160 	const char * passwd;
161 	const char * salt;
162 	uint64_t N;
163 	uint32_t r;
164 	uint32_t p;
165 	uint8_t result[TESTLEN];
166 } testcase = {
167 	"pleaseletmein",
168 	"SodiumChloride",
169 	16,
170 	8,
171 	1,
172 	{
173 		0x25, 0xa9, 0xfa, 0x20, 0x7f, 0x87, 0xca, 0x09,
174 		0xa4, 0xef, 0x8b, 0x9f, 0x77, 0x7a, 0xca, 0x16,
175 		0xbe, 0xb7, 0x84, 0xae, 0x18, 0x30, 0xbf, 0xbf,
176 		0xd3, 0x83, 0x25, 0xaa, 0xbb, 0x93, 0x77, 0xdf,
177 		0x1b, 0xa7, 0x84, 0xd7, 0x46, 0xea, 0x27, 0x3b,
178 		0xf5, 0x16, 0xa4, 0x6f, 0xbf, 0xac, 0xf5, 0x11,
179 		0xc5, 0xbe, 0xba, 0x4c, 0x4a, 0xb3, 0xac, 0xc7,
180 		0xfa, 0x6f, 0x46, 0x0b, 0x6c, 0x0f, 0x47, 0x7b,
181 	}
182 };
183 
184 static int
185 testsmix(void (*smix)(uint8_t *, size_t, uint64_t, void *, void *))
186 {
187 	uint8_t hbuf[TESTLEN];
188 
189 	/* Perform the computation. */
190 	if (_crypto_scrypt(
191 	    (const uint8_t *)testcase.passwd, strlen(testcase.passwd),
192 	    (const uint8_t *)testcase.salt, strlen(testcase.salt),
193 	    testcase.N, testcase.r, testcase.p, hbuf, TESTLEN, smix))
194 		return (-1);
195 
196 	/* Does it match? */
197 	return (memcmp(testcase.result, hbuf, TESTLEN));
198 }
199 
200 static void
201 selectsmix(void)
202 {
203 	/* If generic smix works, use it. */
204 	if (!testsmix(crypto_scrypt_smix)) {
205 		smix_func = crypto_scrypt_smix;
206 		return;
207 	}
208 
209 	/* If we get here, something really bad happened. */
210 	abort();
211 }
212 
213 /**
214  * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen):
215  * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r,
216  * p, buflen) and write the result into buf.  The parameters r, p, and buflen
217  * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32.  The parameter N
218  * must be a power of 2 greater than 1.
219  *
220  * Return 0 on success; or -1 on error.
221  */
222 int
223 crypto_scrypt(const uint8_t * passwd, size_t passwdlen,
224     const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t _r, uint32_t _p,
225     uint8_t * buf, size_t buflen)
226 {
227 
228 	if (smix_func == NULL)
229 		selectsmix();
230 
231 	return (_crypto_scrypt(passwd, passwdlen, salt, saltlen, N, _r, _p,
232 	    buf, buflen, smix_func));
233 }
234