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