xref: /haiku/src/system/libroot/posix/crypt/crypt.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
1 /*
2  * Copyright 2017, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  * 		Andrew Aldridge, i80and@foxquill.com
7  */
8 
9 
10 #include <assert.h>
11 #include <crypt.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <inttypes.h>
15 #include <math.h>
16 #include <stdio.h>
17 #include <string.h>
18 
19 #include <SupportDefs.h>
20 
21 #include "../musl/crypt/crypt_des.h"
22 #include "crypto_scrypt.h"
23 
24 #define SALT_BYTES 32
25 #define SALT_STR_BYTES (SALT_BYTES * 2 + 1)
26 #define DEFAULT_N_LOG2 14
27 
28 #define CRYPT_OUTPUT_BYTES (6 + 64 + 1 + 64 + 1)
29 #define SALT_OUTPUT_BYTES (6 + 64 + 1 + 1)
30 
31 static const char* kHexAlphabet = "0123456789abcdef";
32 static const int8 kHexLookup[] = {
33 	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
34 	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
35 	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3,
36 	4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
37 	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
38 	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15};
39 
40 
41 static int
42 toHex(const uint8* buffer, size_t bufferLength, char* outBuffer,
43 	size_t outBufferLength)
44 {
45 	size_t i;
46 	size_t outIndex = 0;
47 
48 	if (outBufferLength <= bufferLength * 2) {
49 		outBuffer[0] = '\0';
50 		return 1;
51 	}
52 
53 	for (i = 0; i < bufferLength; i += 1) {
54 		const uint8 n = buffer[i];
55 		const uint8 upper = n >> 4;
56 		const uint8 lower = n & 0x0f;
57 
58 		assert(lower < 16 && upper < 16);
59 		outBuffer[outIndex++] = kHexAlphabet[upper];
60 		outBuffer[outIndex++] = kHexAlphabet[lower];
61 		outBuffer[outIndex] = '\0';
62 	}
63 
64 	outBuffer[outIndex] = '\0';
65 
66 	return 0;
67 }
68 
69 
70 static size_t
71 fromHex(const char* hex, uint8* outBuffer, size_t outBufferLength)
72 {
73 	size_t i = 0;
74 	size_t outIndex = 0;
75 
76 	if (hex[0] == '\0' || outBufferLength == 0)
77 		return 0;
78 
79 	while (hex[i] != '\0' && hex[i + 1] != '\0') {
80 		const uint8 char1 = hex[i];
81 		const uint8 char2 = hex[i + 1];
82 
83 		if (char1 >= sizeof(kHexLookup) || char2 >= sizeof(kHexLookup))
84 			return outIndex;
85 
86 		const char index1 = kHexLookup[char1];
87 		const char index2 = kHexLookup[char2];
88 
89 		if (outIndex >= outBufferLength)
90 			return 0;
91 
92 		outBuffer[outIndex++] = (index1 << 4) | index2;
93 		i += 2;
94 	}
95 
96 	return outIndex;
97 }
98 
99 
100 //! Generate a new salt appropriate for crypt().
101 static int
102 crypt_gensalt_rn(char *outbuf, size_t bufsize)
103 {
104 	uint8 salt[SALT_BYTES];
105 	char saltString[SALT_STR_BYTES];
106 	size_t totalBytesRead = 0;
107 
108 	int fd = open("/dev/random", O_RDONLY, 0);
109 	if (fd < 0)
110 		return -1;
111 
112 	while (totalBytesRead < sizeof(salt)) {
113 		const ssize_t bytesRead = read(fd,
114 			static_cast<void*>(salt + totalBytesRead),
115 			sizeof(salt) - totalBytesRead);
116 		if (bytesRead <= 0) {
117 			close(fd);
118 			return -1;
119 		}
120 
121 		totalBytesRead += bytesRead;
122 	}
123 	close(fd);
124 
125 	assert(toHex(salt, sizeof(salt), saltString, sizeof(saltString)) == 0);
126 	snprintf(outbuf, bufsize, "$s$%d$%s$", DEFAULT_N_LOG2, saltString);
127 	return 0;
128 }
129 
130 
131 extern "C" char *
132 _crypt_rn(const char* key, const char* setting, struct crypt_data* data, size_t size)
133 {
134 	uint8 saltBinary[SALT_BYTES];
135 	char saltString[SALT_STR_BYTES];
136 	char gensaltResult[SALT_OUTPUT_BYTES];
137 	uint8 resultBuffer[32];
138 	char hexResultBuffer[64 + 1];
139 	int nLog2 = DEFAULT_N_LOG2;
140 
141 	if (setting == NULL) {
142 		int res = crypt_gensalt_rn(gensaltResult, sizeof(gensaltResult));
143 
144 		// crypt_gensalt_r should set errno itself.
145 		if (res < 0)
146 			return NULL;
147 
148 		setting = gensaltResult;
149 	}
150 
151 	// Some idioms existed where the password was also used as the salt.
152 	// As a crude heuristic, use the old crypt algorithm if the salt is
153 	// shortish.
154 	if (strlen(setting) < 16)
155 		return _crypt_des_r(key, setting, data->buf);
156 
157 	// We don't want to fall into the old algorithm by accident somehow, so
158 	// if our salt is kind of like our salt, but not exactly, return an
159 	// error.
160 	if (sscanf(setting, "$s$%2d$%64s$", &nLog2, saltString) != 2) {
161 		errno = EINVAL;
162 		return NULL;
163 	}
164 
165 	// Set a lower bound on N_log2: below 12 scrypt is weaker than bcrypt.
166 	if (nLog2 < 12) {
167 		errno = EINVAL;
168 		return NULL;
169 	}
170 
171 	size_t saltBinaryLength = fromHex(saltString, saltBinary,
172 		sizeof(saltBinary));
173 	if (saltBinaryLength != sizeof(saltBinary)) {
174 		errno = EINVAL;
175 		return NULL;
176 	}
177 
178 	long n = static_cast<long>(pow(2, nLog2));
179 	if (crypto_scrypt(reinterpret_cast<const uint8*>(key), strlen(key),
180 		saltBinary, saltBinaryLength, n, 8, 1, resultBuffer,
181 		sizeof(resultBuffer)) != 0) {
182 		// crypto_scrypt sets errno itself
183 		return NULL;
184 	}
185 
186 	assert(toHex(resultBuffer, sizeof(resultBuffer), hexResultBuffer,
187 		sizeof(hexResultBuffer)) == 0);
188 	snprintf(data->buf, size - sizeof(int), "$s$%d$%s$%s", nLog2, saltString,
189 		hexResultBuffer);
190 
191 	return data->buf;
192 }
193 
194 
195 char *
196 crypt(const char* key, const char* salt)
197 {
198 	static struct crypt_data data;
199 	return _crypt_rn(key, salt, &data, sizeof(struct crypt_data));
200 }
201 
202 
203 //! To make fcrypt users happy. They don't need to call init_des.
204 char*
205 fcrypt(const char* key, const char* salt)
206 {
207 	return crypt(key, salt);
208 }
209