示例#1
0
/**
 * @brief	Perform a case-sensitive comparison of two managed strings.
 * @param	a	the first managed string to be compared.
 * @param	b	the second managed string to be compared.
 * @result	-1 if a < b, 1 if b < a, or 0 if the two memory blocks are equal.
 */
int_t st_cmp_cs_eq(stringer_t *a, stringer_t *b) {

	bool_t ae, be;
	int_t result = 0;
	uchr_t *aptr, *bptr;
	size_t alen, blen, check;

	// Setup.
	ae = st_empty_out(a, &aptr, &alen);
	be = st_empty_out(b, &bptr, &blen);

	// Empty string checks.
	if (ae && be) return 0;
	else if (ae) return -1;
	else if (be) return 1;

	// Calculate how many bytes to compare.
	check = (alen <= blen ? alen : blen);

	// Break on the first non matching byte.
	for (size_t i = 0; result == 0 && i < check; i++) {
		if (*aptr < *bptr) result = -1;
		else if (*aptr > *bptr) result = 1;
		aptr++;
		bptr++;
	}

	// If the strings match, then the longer string is greater
	if (result == 0 && alen < blen) result =  -1;
	else if (result == 0 && alen > blen) result = 1;
	return result;
}
示例#2
0
/**
 * @brief	Perform a case-insensitive check to see if one string starts with another.
 * @param	s		the managed string to have its beginning characters examined.
 * @param	starts	the managed string to be compared against the beginning of s.
 * @return	-1 if s < starts, 1 if starts < s or 0 if s begins with starts.
 */
int_t st_cmp_ci_starts(stringer_t *s, stringer_t *starts) {

	bool_t se, starte;
	int_t result = 0;
	uchr_t *sptr, *startptr;
	size_t slen, startlen, check;

	// Setup.
	se = st_empty_out(s, &sptr, &slen);
	starte = st_empty_out(starts, &startptr, &startlen);

	// Empty string checks.
	if (se && starte) return 0;
	else if (se) return -1;
	else if (starte) return 1;

	// Calculate how many bytes to compare.
	check = (slen <= startlen ? slen : startlen);

	// Break on the first non matching byte.
	for (size_t i = 0; result == 0 && i < check; i++) {
		if (lower_chr(*sptr) < lower_chr(*startptr)) result = -1;
		else if (lower_chr(*sptr) > lower_chr(*startptr)) result = 1;
		sptr++;
		startptr++;
	}

	// If the string length is equal/greater and result is still set to 0, we have a match.
	if (result == 0 && slen < startlen) result = -1;
	return result;
}
示例#3
0
/**
 * @brief	Search for a character inside of a managed string, and save its location.
 * @param	haystack	the managed string to be searched.
 * @param	needle		the character to be found in the string.
 * @param	location	if not NULL, a pointer to store the index of needle if found, or 0 on no match.
 * @return	true if the specified character was found or false otherwise.
 */
bool_t st_search_chr(stringer_t *haystack, chr_t needle, size_t *location) {

	uchr_t *h;
	size_t hlen;

	if (st_empty_out(haystack, &h, &hlen)) {
		log_pedantic("Passed an empty string.");
		return false;
	}

	// If a location was provided for storing the position of needle, store reset it to zero in case needle isn't found.
	if (location) {
		*location = 0;
	}

	for (size_t i = 0; i < hlen; i++) {

		if (h[i] == needle) {

			if (location) {
				*location = i;
			}

			return true;
		}

	}

	return false;
}
示例#4
0
/**
 * @brief	Check a URL string for validity.
 * @note	This function confirms that the URL consists of only legal characters and that all escaped sequences are also valid.
 * @param	s       a managed string containing the URL to be verified.
 * @return	0 on failure, or the number of valid characters in the URL if the string is valid.
 */
size_t url_valid_st(stringer_t *s) {

	uchr_t *p;
	size_t c = 0, len;
	bool_t result = true;

	if (st_empty_out(s, &p, &len)) {
		return 0;
	}

	// Iterates through and counts valid characters.
	while (result && len) {
		if (url_valid_chr(*p)) {
			len--;
			c++;
			p++;
		}
		// If an invalid character is found check whether its a properly formed escape sequence.
		else if (len >= 3 && *p == '%' && hex_valid_chr(*(p + 1)) && hex_valid_chr(*(p + 2))) {
			len -= 3;
			p += 3;
			c++;
		} else {
			result = false;
			len = 0;
		}
	}

	return c;
}
示例#5
0
/**
 * @brief	Count the number of "Received:" lines in a mail message.
 * @param	message		a managed string containing the mail message to be scanned.
 * @return	the number of matching lines found, or 0 on failure.
 */
uint32_t mail_count_received(stringer_t *message) {

	size_t len;
	uchr_t *ptr;
	uint32_t result = 0;

	if (st_empty_out(message, &ptr, &len) || len <= 9) {
		log_pedantic("Message is too short to hold any valid received lines.");
		return result;
	}

	// Loop through and check every new line.
	// LOW: This loop could be improved using the new string interfaces.
	for (size_t i = 0; i <= (len - 9); i++) {

		// Make sure we detect the end of a header on messages without headers.
		if (*ptr == '\n' || i == 0) {

			// Detect the end of a header.
			if (*(ptr + 1) == '\r' && *(ptr + 2) == '\n') {
				i = len;
			} else if (*(ptr + 1) == '\n') {
				i = len;
			} else if (st_cmp_ci_starts(PLACER(ptr + 1, len - i - 1), PLACER("Received:", 9)) == 0) {
				result++;
			}

		}
		ptr++;
	}

	return result;
}
示例#6
0
/**
 * @brief	Encode a MIME part for a provided block of data (file attachment) with the specified filename.
 * @note	This function will look up the media type based on the supplied filename, and use that media type as a determination
 * 			of whether the content is to be encoded as either quoted-printable or base64.
 * @param	data		a pointer to a managed string containing the body of the data to be encoded.
 * @param	filename	a pointer to a managed string containing the filename of the attachment for which the data was provided.
 * @param	boundary	a pointer to a managed string containing the boundary that will be used to separate the individual MIME parts.
 * @return	NULL on failure, or a pointer to a managed string containing the file attachment encoded as a MIME part on success.
 */
stringer_t * mail_mime_encode_part(stringer_t *data, stringer_t *filename, stringer_t *boundary) {

	stringer_t *result, *encoded;
	media_type_t *mtype;
	chr_t *fstart, *extptr = NULL, *ctype;
	size_t flen;

	if (!data) {
		return NULL;
	}

	// First get the extension of the filename so we can look up the media type.
	if (!st_empty_out(filename, (uchr_t **)&fstart, &flen)) {
		extptr = fstart + flen + 1;

		while (extptr >= fstart) {

			if (*extptr == '.') {
				break;
			}

			extptr--;
		}

		if (extptr < fstart) {
			extptr = NULL;
		}

	}

	mtype = mail_mime_get_media_type (extptr);

	if (mtype->bin) {
		encoded = base64_encode(data, NULL);
		ctype = "base64";
	} else {
		encoded = qp_encode(data);
		ctype = "quoted-printable";
	}

	if (!encoded) {
		log_pedantic("Unable to encode MIME part data.");
		return NULL;
	}

	// What we return is: boundary/CRLF, Content-Type/CRLF, Content-Transfer-Encoding/CRLF, Content-Disposition/CRLF, data/CRLF
	if (!(result = st_merge("nsnnnnnnnsns", "--------------", boundary, "\r\n", "Content-Type: ", mtype->name, ";\r\n", "Content-Transfer-Encoding: ", ctype,
			"\r\nContent-Disposition: attachment; filename=\"", filename, "\"\r\n\r\n", encoded))) {
		log_pedantic("Unable to generate MIME part data.");
		return NULL;
	}

	st_free(encoded);

	return result;
}
示例#7
0
/**
 * @brief	Search one managed string for an occurrence of another in a case-sensitive manner, and save its location.
 * @param	haystack	the managed string to be searched.
 * @param	needle		the managed string to be found.
 * @param	location	if not NULL, a pointer to store the index of needle if found, or 0 on no match.
 * @return	true if the string is found or false otherwise.
 */
bool_t st_search_cs(stringer_t *haystack, stringer_t *needle, size_t *location) {

	uchr_t *h, *n;
	bool_t result = false;
	size_t i, j, hlen, nlen;

	if (st_empty_out(haystack, &h, &hlen) || st_empty_out(needle, &n, &nlen)) {
		log_pedantic("Passed an empty string.");
		return false;
	} else if (nlen > hlen) {
		return false;
	}

	// If a location was provided for storing the position of needle, store reset it to zero in case needle isn't found.
	if (location) {
		*location = 0;
	}

	// The needle will never be found if it's longer than the haystack.
	if (nlen > hlen) {
		return false;
	}

	for (i = 0; i <= hlen - nlen && !result; i++) {

		for (j = 0; j < nlen && (*(h + j) == *(n + j)); j++);

		// In theory, j won't ever equal nlen if a non-matching character is found.
		if (j == nlen && location) {
			result = true;
			*location = i;
		}
		else if (j == nlen) {
			result = true;
		}
		else {
			h++;
		}

	}

	return result;
}
示例#8
0
文件: hex.c 项目: lavabit/magma
/**
 * @brief	Convert a hex string into a binary data blob.
 * @note	All hex strings should be composed of pairs of two hex characters representing individual bytes.
 * 			Invalid hex characters will simply be ignored during processing.
 * @param	h		a managed string containing the input hex string to be decoded.
 * @param	output	if not NULL, a pointer to a managed string to contain the decoded binary output; if NULL, a new string
 * 					will be allocated and returned to the caller.
 * @return	a pointer to a managed string containing the decoded output, or NULL on failure.
 */
stringer_t * hex_decode_st(stringer_t *h, stringer_t *output) {

	uint32_t opts = 0;
	uchr_t *p = NULL, *o, c = 0;
	size_t w = 0, len = 0, valid;
	stringer_t *result = NULL;

	if (output && !st_valid_destination((opts = *((uint32_t *)output)))) {
		log_pedantic("An output string was supplied but it does not represent a buffer capable of holding the output.");
		return NULL;
	}
	else if (st_empty_out(h, &p, &len) || !(valid = hex_count_st(h))) {
		log_pedantic("The input block does not appear to hold any data ready for decoding. {%slen = %zu}", p ? "" : "p = NULL / ", len);
		return NULL;
	}

	// Make sure the output buffer is large enough or if output was passed in as NULL we'll attempt the allocation of our own buffer.
	if ((result = output) && ((st_valid_avail(opts) && st_avail_get(output) < (valid / 2)) ||
			(!st_valid_avail(opts) && st_length_get(output) < (valid / 2)))) {
		log_pedantic("The output buffer supplied is not large enough to hold the result. {avail = %zu / required = %zu}",
				st_valid_avail(opts) ? st_avail_get(output) : st_length_get(output), valid / 2);
		return NULL;
	}
	else if (!output && !(result = st_alloc(valid / 2))) {
		log_pedantic("The output buffer memory allocation request failed. {requested = %zu}", (valid / 2));
		return NULL;
	}

	// Store the memory address where the output should be written.
	o = st_data_get(result);

	// Loop through the input buffer and translate valid characters into a binary octet.
	for (size_t i = 0; i < len; i++) {
		if (hex_valid_chr(*p)) {
			if (!c) {
				c = *p;
			}
			else {
				*o++ = hex_decode_chr(c, *p);
				c = 0;
				w++;
			}
		}
		p++;
	}

	// If an output buffer was supplied that is capable of tracking the data length, or a managed string buffer was allocated update the length param.
	if (!output || st_valid_tracked(opts)) {
		st_length_set(result, w);
	}

	return result;
}
示例#9
0
/**
 * @brief	Encode a data buffer as a valid URL component.
 * @param	s       a managed string containing the data to be encoded.
 * @return	NULL on failure, or a freshly allocated managed string containing the fully-escaped string suitable for use in a URL.
 */
stringer_t *url_encode(stringer_t *s) {

	chr_t hex[4];
	uchr_t *p;
	stringer_t *output, *r;
	size_t len, expected = 0, written = 0;

	if (st_empty_out(s, &p, &len)) {
		log_pedantic("An empty string was passed in for encoding.");
		return NULL;
	}

	// Increment through the stringer and count the characters that need to be encoded.
	for (size_t i = 0; i < len; i++) {
		if (url_valid_chr(*p)) {
			expected++;
		} else {
			expected += 3;
		}

		p++;
	}

	// Allocate one byte for printable characters and three bytes for non-printable characters.
	if (!(output = st_alloc_opts(MANAGED_T | JOINTED | HEAP, expected))) {
		log_pedantic("Could not allocate a buffer large enough to hold encoded result. {requested = %zu}", expected);
		return NULL;
	}

	// Get setup.
	p = st_data_get(s);

	// Increment through the stringer and copy the data into the new stringer.
	for (size_t i = 0; i < len; i++) {

		// Escape the invalid characters.
		if (url_valid_chr(*p)) {
			if ((r = st_append(output, PLACER(p, 1)))) {
				output = r;
				written++;
			}
		} else if (snprintf(hex, 4, "%%%02X", *p) == 3 && (r = st_append(output, PLACER(&hex[0], 3)))) {
			output = r;
			written += 3;
		}

		// We always advance the input pointer.
		p++;
	}

	st_length_set(output, written);
	return output;
}
示例#10
0
/**
 * @brief	Count the number of hex characters in a string.
 * @param	s	the managed string to be scanned.
 * @return	the number of valid hexadecimal characters found in the string.
 */
size_t hex_count_st(stringer_t *s) {

	uchr_t *p;
	size_t c = 0, len;

	if (st_empty_out(s, &p, &len)) {
		return 0;
	}

	// Iterates through and counts valid characters. If an invalid character is found the loop is broken.
	for (size_t i = 0; i < len; i++) {
		if (hex_valid_chr(*p++)) c++;
	}

	return c;
}
示例#11
0
文件: hex.c 项目: lavabit/magma
/**
 * @brief	Convert a block of binary data into a hex string.
 * @param	b		a managed string containing the raw data to be encoded.
 * @param	output	if not NULL, a pointer to a managed string that will store the encoded output; if NULL, a new managed string will
 * 					be allocated and returned to the caller.
 * @return 	NULL on failure, or a pointer to a managed string containing the hex-encoded output on success.
 */
stringer_t * hex_encode_st(stringer_t *b, stringer_t *output) {

	size_t len = 0;
	uint32_t opts = 0;
	uchr_t *p = NULL, *o;
	stringer_t *result = NULL;

	if (output && !st_valid_destination((opts = *((uint32_t *)output)))) {
		log_pedantic("An output string was supplied but it does not represent a buffer capable of holding the output.");
		return NULL;
	}
	else if (st_empty_out(b, &p, &len)) {
		log_pedantic("The input block does not appear to hold any data ready for encoding. {%slen = %zu}", p ? "" : "p = NULL / ", len);
		return NULL;
	}

	// Make sure the output buffer is large enough or if output was passed in as NULL we'll attempt the allocation of our own buffer.
	if ((result = output) && ((st_valid_avail(opts) && st_avail_get(output) < (len * 2)) ||
			(!st_valid_avail(opts) && st_length_get(output) < (len * 2)))) {
		log_pedantic("The output buffer supplied is not large enough to hold the result. {avail = %zu / required = %zu}",
				st_valid_avail(opts) ? st_avail_get(output) : st_length_get(output), len * 2);
		return NULL;
	}
	else if (!output && !(result = st_alloc(len * 2))) {
		log_pedantic("The output buffer memory allocation request failed. {requested = %zu}", len * 2);
		return NULL;
	}

	// Store the memory address where the output should be written.
	o = st_data_get(result);

	// Loop through the input buffer and write character pairs to the result string data buffer.
	for (size_t i = 0; i < len; i++) {
		hex_encode_chr(*p, o);
		o += 2;
		p += 1;
	}

	// If an output buffer was supplied that is capable of tracking the data length, or a managed string buffer was allocated update the length param.
	if (!output || st_valid_tracked(opts)) {
		st_length_set(result, len * 2);
	}

	return result;
}
示例#12
0
文件: replace.c 项目: lavabit/magma
/**
 * @brief	Replace all instances of one character in a managed string with another.
 * @param	target		the input string which will be transformed by the character replacement.
 * @param	pattern		the character to be searched and replaced in the target string.
 * @param	replacement	the character to be substituted for the pattern character in the target string.
 * @return	a pointer to the target managed string.
 */
stringer_t * st_swap(stringer_t *target, uchr_t pattern, uchr_t replacement) {

	size_t tlen;
	uchr_t *tptr;

	if (st_empty_out(target, &tptr, &tlen)) {
		log_pedantic("Sanity check failed. Passed a NULL pointer.");
		return target;
	}

	log_check(pattern == replacement);

	// Increment through and replace the pattern.
	for (size_t i = 0; i < tlen; i++) {
		if (*tptr == pattern) *tptr = replacement;
		tptr++;
	}

	return target;
}
示例#13
0
/**
 * @brief	Determine whether a managed string is a properly formatted hex string and return the number found.
 * @note	A valid hex string consists of only hexadecimal characters and whitespace, and the number of hex characters is divisible by two.
 * @param	s	the managed string to be tested.
 * @return	0 on failure, or the number of hexadecimal characters found in the managed string.
 */
size_t hex_valid_st(stringer_t *s) {

	uchr_t *p;
	size_t c = 0, len;
	bool_t result = true;

	if (st_empty_out(s, &p, &len)) {
		return 0;
	}

	// Iterates through and counts valid characters. If an invalid character is found the loop is broken.
	for (size_t i = 0; result && i < len; i++) {
		if (hex_valid_chr(*p)) c++;
		else if (*p != ' ' && *p != '\t' && *p != '\r' && *p != '\n') result = false;
		p++;
	}

	// This check ensures the number of valid characters is at least two and evenly divisible by two.
	if (!result || (c % 2)) {
		c = 0;
	}

	return c;
}
示例#14
0
文件: qp.c 项目: lavabit/magma
/**
 * @brief	Perform QP (quoted-printable) encoding of a string.
 * @param	s	a pointer to a managed string containing data to be encoded.
 * @return	a pointer to a managed string containing the QP encoded data, or NULL on failure.
 */
stringer_t * qp_encode(stringer_t *s) {

	chr_t hex[4];
	uchr_t *p;//, *o;
	stringer_t *output, *r;
	size_t len, expected = 0, line = 0;

	if (st_empty_out(s, &p, &len)) {
		log_pedantic("An empty string was passed in for encoding.");
		return NULL;
	}

	// Increment through the stringer and count the characters that need to be encoded.
	for (size_t i = 0; i < len; i++) {

		if (*p < '!' || *p > '~' || *p == '=' || *p == ' ' || *p == '\r' || *p == '\n' || *p == '\t') {
			expected += 3;
		}
		else {
			expected++;
		}

		p++;
	}

	// Include room for the soft line break sequence every seventy six characters.
	expected += ((expected + QP_LINE_WRAP_LENGTH) / QP_LINE_WRAP_LENGTH) * 3;

	// Allocate one byte for printable characters and three bytes for non-printable characters.
	if (!(output = st_alloc_opts(MANAGED_T | JOINTED | HEAP, expected))) {
		log_pedantic("Could not allocate a buffer large enough to hold encoded result. {requested = %zu}", expected);
		return NULL;
	}

	// Get setup.
	p = st_data_get(s);
	//o = st_data_get(output);

	// Increment through the stringer and copy the data into the new stringer.
	for (size_t i = 0; i < len; i++) {

		// Escape the characters matching this boolean while simply copying any other characters we encounter.
		if (*p < '!' || *p > '~' || *p == '=' || *p == ' ' || *p == '\r' || *p == '\n' || *p == '\t') {

			// If were within three characters of the limit append a soft line break to the buffer.
			if (line > (QP_LINE_WRAP_LENGTH - 3) && snprintf(hex, 4, "=\r\n") == 3 && (r = st_append(output, PLACER(&hex[0], 3)))) {
				output = r;
				line = 0;
			}

			if (snprintf(hex, 4, "=%02X", *p) == 3 && (r = st_append(output, PLACER(&hex[0], 3)))) {
				output = r;
				line += 3;
			}
		}
		else {

			// If were near the line length limit this will append a soft line break before appending the next character.
			if (line > (QP_LINE_WRAP_LENGTH - 1) && snprintf(hex, 4, "=\r\n") == 3 && (r = st_append(output, PLACER(&hex[0], 3)))) {
				output = r;
				line = 0;
			}

			if ((r = st_append(output, PLACER(p, 1)))) {
				output = r;
				line++;
			}

		}

		// We always advance the input pointer.
		p++;
	}

	return output;
}
示例#15
0
文件: qp.c 项目: lavabit/magma
/**
 * @brief	Perform QP (quoted-printable) decoding of a string.
 * @param	s	the managed string containing data to be decoded.
 * @return	a pointer to a managed string containing the 8-bit decoded output, or NULL on failure.
 */
stringer_t * qp_decode(stringer_t *s) {

	uchr_t *p, *o;
	stringer_t *output;
	size_t len, written = 0;

	if (st_empty_out(s, &p, &len)) {
		log_pedantic("An empty string was passed in for decoding.");
		return NULL;
	}

	// Allocate one byte for printable characters and three bytes for non-printable characters.
	if (!(output = st_alloc(len))) {
		log_pedantic("Could not allocate a buffer large enough to hold decoded result. {requested = %zu}", len);
		return NULL;
	}

	// Get setup.
	o = st_data_get(output);

#ifdef MAGMA_PEDANTIC
	// In pedantic mode we perform an extra check to make sure the loop doesn't loop past zero.
	while (len && len <= st_length_get(s)) {
#else
	while (len) {
#endif

		// Advance past the trigger.
		if (*p == '=') {

			len--;
			p++;

			// Valid hex pair.
			if (len >= 2 && hex_valid_chr(*p) && hex_valid_chr(*(p + 1))) {
				*o++ = hex_decode_chr(*p, *(p + 1));
				written++;
				len -= 2;
				p += 2;
			}
			// Soft line breaks are signaled by a line break following an equal sign.
			else if (len >= 2 && *p == '\r' && *(p + 1) == '\n') {
				len -= 2;
				p += 2;
			}
			else if (len >= 1 && *p == '\n') {
				len--;
				p++;
			}
			// Equal signs which aren't followed by a valid hex pair or a line break are illegal, but if the character is printable
			// we can let through the original sequence.
			else if (len >= 1 && ((*p >= '!' && *p <= '<') || (*p >= '>' && *p <= '~'))) {
				*o++ = '=';
				*o++ = *p++;
				written += 2;
				len--;
			}
			// Characters outside the printable range are simply skipped.
			else if (len >= 1) {
				len--;
				p++;
			}
		}
		// Let through any characters found inside this range.
		else if ((*p >= '!' && *p <= '<') || (*p >= '>' && *p <= '~')) {
			*o++ = *p++;
			written++;
			len--;
		}
		// Characters outside the range above should have been encoded. Any that weren't should be skipped.
		else {
			len--;
			p++;
		}

	}

	// We allocated a default string buffer, which means the length is tracked so we need to set the data length.
	st_length_set(output, written);
	return output;
}
示例#16
0
void imap_command_log_safe(stringer_t *line) {

	uchr_t *stream, *copy;
	size_t len, loc = 0;
	int_t i;

	if (st_empty_out(line, &stream, &len)) {
		return;
	}

	// A command string that doesn't even contain "LOGIN" is inherently "safe".
	if (!st_search_ci(line, PLACER("LOGIN", 5), &loc) || !loc) {
		log_info("%.*s", st_length_int(line), st_char_get(line));
		return;
	}


	// The LOGIN command should have been preceded by a whitespace.
	if (!chr_whitespace(stream[loc-1])) {
		log_info("%.*s", st_length_int(line), st_char_get(line));
		return;
	}

	// There should be should only be ONE more non-whitespace tag before the LOGIN command.
	for (i = loc-1; i >= 0; i--) {

		if (!chr_whitespace(stream[i])) {
			break;
		}

	}

	if (i < 0) {
		log_info("%.*s", st_length_int(line), st_char_get(line));
		return;
	}

	while (i >= 0) {

		if (chr_whitespace(stream[i])) {
			log_info("%.*s", st_length_int(line), st_char_get(line));
			return;
		}
			
		i--;
	}

	if (!(copy = mm_dupe(stream, len))) {
		return;
	}

	// Skip past "LOGIN" ...
	i = loc + 5;

	// and the trailing spaces.
	while ((i < len) && chr_whitespace(stream[i])) {
		i++;
	}

	if (i == len) {
		log_info("%.*s", st_length_int(line), st_char_get(line));
		mm_free(copy);
		return;
	}

	// The next parameter is the username. Skip past that as well.
	while ((i < len) && !chr_whitespace(stream[i])) {
		i++;
	}

	while ((i < len) && chr_whitespace(stream[i])) {
		i++;
	}

	if (i == len) {
		log_info("%.*s", st_length_int(line), st_char_get(line));
		mm_free(copy);
		return;
	}

	for(;i < len; i++) {
		copy[i] = '*';
	}

	log_info("%.*s", (int)len, copy);
	mm_free(copy);

	return;
}
示例#17
0
文件: ecies.c 项目: lavabit/magma
/**
 * @brief	Decrypt a block of data using an ECIES private key.
 * @param	key			the ECIES private key in the specified format.
 * @param	key_type	the encoding type of the ECIES private key (ECIES_PRIVATE_BINARY or ECIES_PRIVATE_HEX).
 * @param	cryptex		a pointer to the head of the cryptex object with the encrypted data.
 * @param	length		a pointer to a size_t variable which will receive the final length of the unencrypted data.
 * @return	NULL on failure, or a pointer to a memory address containing the decrypted data on success..
 */
uchr_t * deprecated_ecies_decrypt(stringer_t *key, ECIES_KEY_TYPE key_type, cryptex_t *cryptex, size_t *length) {

	HMAC_CTX hmac;
	size_t key_length;
	int output_length;
	EVP_CIPHER_CTX cipher;
	EC_KEY *user, *ephemeral;
	uchr_t *kbuf;
	size_t hexkey_length;
	unsigned int mac_length = EVP_MAX_MD_SIZE;
	unsigned char envelope_key[SHA512_DIGEST_LENGTH], iv[EVP_MAX_IV_LENGTH], md[EVP_MAX_MD_SIZE], *block, *output;

	// Simple sanity check.
	if (!key || !cryptex || !length) {
		log_info("Invalid parameters passed in.");
		return NULL;
	}
	else if ((key_type != ECIES_PRIVATE_HEX) && (key_type != ECIES_PRIVATE_BINARY)) {
		log_info("Invalid ecies private key type specified!");
		return NULL;
	}
	else if (st_empty_out(key,&kbuf,&hexkey_length)) {
		log_info("Could not read key data.");
		return NULL;
	}
	// Make sure we are generating enough key material for the symmetric ciphers.
	else if ((key_length = EVP_CIPHER_key_length_d(EVP_get_cipherbyname_d(OBJ_nid2sn_d(ECIES_CIPHER)))) * 2 > SHA512_DIGEST_LENGTH) {
		log_info("The key derivation method will not produce enough envelope key material for the chosen ciphers. {envelope = %i / required = %zu}", SHA512_DIGEST_LENGTH / 8, (key_length * 2) / 8);
		return NULL;
	}
	// Convert the user's public key from hex into a full EC_KEY structure.
	else if (!(user = deprecated_ecies_key_private(key_type, pl_init(kbuf, hexkey_length)))) {
		log_info("Invalid private key provided.");
		return NULL;
	}
	// Create the ephemeral key used specifically for this block of data.
	else if (!(ephemeral = deprecated_ecies_key_public(ECIES_PUBLIC_BINARY, pl_init(deprecated_cryptex_envelope_data(cryptex), deprecated_cryptex_envelope_length(cryptex))))) {
		log_info("An error occurred while trying to recreate the ephemeral key.");
		EC_KEY_free_d(user);
		return NULL;
	}
	// Use the intersection of the provided keys to generate the envelope data used by the ciphers below. The ecies_key_derivation() function uses
	// SHA 512 to ensure we have a sufficient amount of envelope key material and that the material created is sufficiently secure.
	else if (ECDH_compute_key_d(envelope_key, SHA512_DIGEST_LENGTH, EC_KEY_get0_public_key_d(ephemeral), user, deprecated_ecies_envelope_derivation) != SHA512_DIGEST_LENGTH) {
		log_info("An error occurred while trying to compute the envelope key. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		EC_KEY_free_d(ephemeral);
		EC_KEY_free_d(user);
		return NULL;
	}

	// The envelope key material has been extracted, so we no longer need the user and ephemeral keys.
	EC_KEY_free_d(ephemeral);
	EC_KEY_free_d(user);

	// Use the authenticated hash of the ciphered data to ensure it was not modified after being encrypted.
	HMAC_CTX_init_d(&hmac);

	// At the moment we are generating the hash using encrypted data. At some point we may want to validate the original text instead.
	if (HMAC_Init_ex_d(&hmac, envelope_key + key_length, key_length, EVP_get_digestbyname_d(OBJ_nid2sn_d(ECIES_HMAC)), NULL) != 1 || HMAC_Update_d(&hmac, deprecated_cryptex_body_data(cryptex), deprecated_cryptex_body_length(cryptex)) != 1 || HMAC_Final_d(&hmac, md, &mac_length) != 1) {
		log_info("Unable to generate the authentication code needed for validation. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		HMAC_CTX_cleanup_d(&hmac);
		return NULL;
	}

	HMAC_CTX_cleanup_d(&hmac);

	// We can use the generated hash to ensure the encrypted data was not altered after being encrypted.
	if (mac_length != deprecated_cryptex_hmac_length(cryptex) || memcmp(md, deprecated_cryptex_hmac_data(cryptex), mac_length)) {
		log_info("The authentication code was invalid! The ciphered data has been corrupted!");
		return NULL;
	}

	// Create a buffer to hold the result.
	output_length = deprecated_cryptex_body_length(cryptex);

	if (!(block = output = mm_alloc(output_length + 1))) {
		log_info("An error occurred while trying to allocate memory for the decrypted data.");
		return NULL;
	}

	// For now we use an empty initialization vector. We also clear out the result buffer just to be on the safe side.
	memset(iv, 0, EVP_MAX_IV_LENGTH);
	memset(output, 0, output_length + 1);

	EVP_CIPHER_CTX_init_d(&cipher);

	// Decrypt the data using the chosen symmetric cipher.
	if (EVP_DecryptInit_ex_d(&cipher, EVP_get_cipherbyname_d(OBJ_nid2sn_d(ECIES_CIPHER)), NULL, envelope_key, iv) != 1 || EVP_CIPHER_CTX_set_padding_d(&cipher, 0) != 1 || EVP_DecryptUpdate_d(&cipher, block, &output_length, deprecated_cryptex_body_data(cryptex),
			deprecated_cryptex_body_length(cryptex)) != 1) {
		log_info("Unable to decrypt the data using the chosen symmetric cipher. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		EVP_CIPHER_CTX_cleanup_d(&cipher);
		free(output);
		return NULL;
	}

	block += output_length;

	if ((output_length = deprecated_cryptex_body_length(cryptex) - output_length) != 0) {
		log_info("The symmetric cipher failed to properly decrypt the correct amount of data!");
		EVP_CIPHER_CTX_cleanup_d(&cipher);
		free(output);
		return NULL;
	}

	if (EVP_DecryptFinal_ex_d(&cipher, block, &output_length) != 1) {
		log_info("Unable to decrypt the data using the chosen symmetric cipher. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		EVP_CIPHER_CTX_cleanup_d(&cipher);
		free(output);
		return NULL;
	}

	EVP_CIPHER_CTX_cleanup_d(&cipher);

	*length = deprecated_cryptex_original_length(cryptex);

	return output;
}
示例#18
0
文件: ecies.c 项目: lavabit/magma
/**
 * @brief	Encrypt a block of data using an ECIES public key.
 * @param	key			the ECIES public key in the specified format.
 * @param	key_type	the encoding type of the ECIES public key (ECIES_PUBLIC_BINARY or ECIES_PUBLIC_HEX).
 * @param	data		a pointer to the block of data to be encrypted.
 * @param	length		the length, in bytes, of the data to be encrypted.
 * @return	NULL on failure, or a pointer to the header of the cryptex object containing the encrypted data on success..
 */
cryptex_t * deprecated_ecies_encrypt(stringer_t *key, ECIES_KEY_TYPE key_type, unsigned char *data, size_t length) {

	void *body;
	HMAC_CTX hmac;
	int body_length;
	cryptex_t *cryptex;
	EVP_CIPHER_CTX cipher;
	unsigned int mac_length;
	EC_KEY *user, *ephemeral;
	size_t envelope_length = 0, block_length = 0, key_length = 0, hexkey_length = 0;
	uchr_t *kbuf;
	unsigned char envelope_key[SHA512_DIGEST_LENGTH], iv[EVP_MAX_IV_LENGTH], block[EVP_MAX_BLOCK_LENGTH];

	// Simple sanity check.
	if (!key || !data || !length) {
		log_info("Invalid parameters passed in.");
		return NULL;
	}
	else if ((key_type != ECIES_PUBLIC_HEX) && (key_type != ECIES_PUBLIC_BINARY)) {
		log_info("Invalid ecies private key type specified!");
		return NULL;
	}
	else if (st_empty_out(key,&kbuf,&hexkey_length)) {
		log_info("Could not read key data.");
		return NULL;
	}

	// Make sure we are generating enough key material for the symmetric ciphers.
	if ((key_length = EVP_CIPHER_key_length_d(EVP_get_cipherbyname_d(OBJ_nid2sn_d(ECIES_CIPHER)))) * 2 > SHA512_DIGEST_LENGTH) {
		log_info("The key derivation method will not produce enough envelope key material for the chosen ciphers. {envelope = %i / required = %zu}", SHA512_DIGEST_LENGTH / 8, (key_length * 2) / 8);
		return NULL;
	}
	// Convert the user's public key from hex into a full EC_KEY structure.
	if (!(user = deprecated_ecies_key_public(key_type, pl_init(kbuf, hexkey_length)))) {
		log_info("Invalid public key provided.");
		return NULL;
	}
	// Create the ephemeral key used specifically for this block of data.
	else if (!(ephemeral = deprecated_ecies_key_create())) {
		log_info("An error occurred while trying to generate the ephemeral key.");
		EC_KEY_free_d(user);
		return NULL;
	}
	// Use the intersection of the provided keys to generate the envelope data used by the ciphers below. The ecies_key_derivation() function uses
	// SHA 512 to ensure we have a sufficient amount of envelope key material and that the material created is sufficiently secure.
	else if (ECDH_compute_key_d(envelope_key, SHA512_DIGEST_LENGTH, EC_KEY_get0_public_key_d(user), ephemeral, deprecated_ecies_envelope_derivation) != SHA512_DIGEST_LENGTH) {
		log_info("An error occurred while trying to compute the envelope key. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		EC_KEY_free_d(ephemeral);
		EC_KEY_free_d(user);
		return NULL;
	}
	// Determine the envelope and block lengths so we can allocate a buffer for the result.
	else if ((block_length = EVP_CIPHER_block_size_d(EVP_get_cipherbyname_d(OBJ_nid2sn_d(ECIES_CIPHER)))) == 0 || block_length > EVP_MAX_BLOCK_LENGTH || (envelope_length = EC_POINT_point2oct_d(EC_KEY_get0_group_d(ephemeral), EC_KEY_get0_public_key_d(ephemeral),
			POINT_CONVERSION_COMPRESSED, NULL, 0, NULL)) == 0) {
		log_info("Invalid block or envelope length. {block = %zu / envelope = %zu}", block_length, envelope_length);
		EC_KEY_free_d(ephemeral);
		EC_KEY_free_d(user);
		return NULL;
	}
	// We use a conditional to pad the length if the input buffer is not evenly divisible by the block size.
	else if (!(cryptex = deprecated_cryptex_alloc(envelope_length, EVP_MD_size_d(EVP_get_digestbyname_d(OBJ_nid2sn_d(ECIES_HMAC))), length, length + (length % block_length ? (block_length - (length % block_length)) : 0)))) {
		log_info("Unable to allocate a secure_t buffer to hold the encrypted result.");
		EC_KEY_free_d(ephemeral);
		EC_KEY_free_d(user);
		return NULL;
	}
	// Store the public key portion of the ephemeral key.
	else if (EC_POINT_point2oct_d(EC_KEY_get0_group_d(ephemeral), EC_KEY_get0_public_key_d(ephemeral), POINT_CONVERSION_COMPRESSED, deprecated_cryptex_envelope_data(cryptex), envelope_length, NULL) != envelope_length) {
		log_info("An error occurred while trying to record the public portion of the envelope key. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		EC_KEY_free_d(ephemeral);
		EC_KEY_free_d(user);
		deprecated_cryptex_free(cryptex);
		return NULL;
	}
	// The envelope key has been stored so we no longer need to keep the keys around.
	EC_KEY_free_d(ephemeral);
	EC_KEY_free_d(user);

	// For now we use an empty initialization vector.
	// LOW: Develop a more secure method for selecting and storing the initialization vector.
	memset(iv, 0, EVP_MAX_IV_LENGTH);

	// Setup the cipher context, the body length, and store a pointer to the body buffer location.
	EVP_CIPHER_CTX_init_d(&cipher);
	body = deprecated_cryptex_body_data(cryptex);
	body_length = deprecated_cryptex_body_length(cryptex);

	// Initialize the cipher with the envelope key.
	if (EVP_EncryptInit_ex_d(&cipher, EVP_get_cipherbyname_d(OBJ_nid2sn_d(ECIES_CIPHER)), NULL, envelope_key, iv) != 1 || EVP_CIPHER_CTX_set_padding_d(&cipher, 0) != 1 ||
			EVP_EncryptUpdate_d(&cipher, body, &body_length, data, length - (length % block_length)) != 1) {
		log_info("An error occurred while trying to secure the data using the chosen symmetric cipher. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		EVP_CIPHER_CTX_cleanup_d(&cipher);
		deprecated_cryptex_free(cryptex);
		return NULL;
	}
	// Check whether all of the data was encrypted. If they don't match up, we either have a partial block remaining, or an error occurred.
	else if (body_length != length) {

		// Make sure all that remains is a partial block, and their wasn't an error.
		if (length - body_length >= block_length) {
			log_info("Unable to secure the data using the chosen symmetric cipher. {%s}", ssl_error_string(MEMORYBUF(256), 256));
			EVP_CIPHER_CTX_cleanup_d(&cipher);
			deprecated_cryptex_free(cryptex);
			return NULL;
		}

		// Copy the remaining data into our partial block buffer. The memset() call ensures any extra bytes will be zero'ed out.
		memset(block, 0, EVP_MAX_BLOCK_LENGTH);
		memcpy(block, data + body_length, length - body_length);
		// Advance the body pointer to the location of the remaining space, and calculate just how much room is still available.
		body += body_length;

		if ((body_length = deprecated_cryptex_body_length(cryptex) - body_length) < 0) {
			log_info("The symmetric cipher overflowed!");
			EVP_CIPHER_CTX_cleanup_d(&cipher);
			deprecated_cryptex_free(cryptex);
			return NULL;
		}
		// Pass the final partially filled data block into the cipher as a complete block. The padding will be removed during the decryption process.
		else if (EVP_EncryptUpdate_d(&cipher, body, &body_length, block, block_length) != 1) {
			log_info("Unable to secure the data using the chosen symmetric cipher. {%s}", ssl_error_string(MEMORYBUF(256), 256));
			EVP_CIPHER_CTX_cleanup_d(&cipher);
			deprecated_cryptex_free(cryptex);
			return NULL;
		}

	}
	// Advance the pointer, then use pointer arithmetic to calculate how much of the body buffer has been used. The complex logic is needed so that we get
	// the correct status regardless of whether there was a partial data block.
	body += body_length;

	if ((body_length = deprecated_cryptex_body_length(cryptex) - (body - deprecated_cryptex_body_data(cryptex))) < 0) {
		log_info("The symmetric cipher overflowed!");
		EVP_CIPHER_CTX_cleanup_d(&cipher);
		deprecated_cryptex_free(cryptex);
		return NULL;
	} else if (EVP_EncryptFinal_ex_d(&cipher, body, &body_length) != 1) {
		log_info("Unable to secure the data using the chosen symmetric cipher. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		EVP_CIPHER_CTX_cleanup_d(&cipher);
		deprecated_cryptex_free(cryptex);
		return NULL;
	}

	EVP_CIPHER_CTX_cleanup_d(&cipher);

	// Generate an authenticated hash which can be used to validate the data during decryption.
	HMAC_CTX_init_d(&hmac);
	mac_length = deprecated_cryptex_hmac_length(cryptex);

	// At the moment we are generating the hash using encrypted data. At some point we may want to validate the original text instead.
	if (HMAC_Init_ex_d(&hmac, envelope_key + key_length, key_length, EVP_get_digestbyname_d(OBJ_nid2sn_d(ECIES_HMAC)), NULL) != 1 || HMAC_Update_d(&hmac, deprecated_cryptex_body_data(cryptex), deprecated_cryptex_body_length(cryptex)) != 1 || HMAC_Final_d(&hmac, deprecated_cryptex_hmac_data(
			cryptex), &mac_length) != 1) {
		log_info("Unable to generate a data authentication code. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		HMAC_CTX_cleanup_d(&hmac);
		deprecated_cryptex_free(cryptex);
		return NULL;
	}

	HMAC_CTX_cleanup_d(&hmac);

	return cryptex;
}
示例#19
0
/**
 * @brief	Decode a URL-encoded string into its original representation.
 * @param	s       a managed string containing the UR componentL to be decoded.
 * @return	NULL on failure, or a freshly allocated managed string containing the original data represented by the URL-encoded input on success.
 */
stringer_t *url_decode(stringer_t *s) {

	uchr_t *p, *o;
	stringer_t *output;
	size_t len, written = 0;

	if (st_empty_out(s, &p, &len)) {
		log_pedantic("An empty string was passed in for decoding.");
		return NULL;
	}

	// Allocate one byte for printable characters and three bytes for non-printable characters.
	if (!(output = st_alloc(len))) {
		log_pedantic("Could not allocate a buffer large enough to hold decoded result. {requested = %zu}", len);
		return NULL;
	}

	// Get setup.
	o = st_data_get(output);


#ifdef MAGMA_PEDANTIC
	// In pedantic mode we perform an extra check to make sure the loop doesn't loop past zero.
	while (len && len <= st_length_get(s)) {
#else
	while (len) {
#endif

		// Advance past the trigger.
		if (*p == '%') {

			len--;
			p++;

			// Valid hex pair.
			if (len >= 2 && hex_valid_chr(*p) && hex_valid_chr(*(p + 1))) {
				*o++ = hex_decode_chr(*p, *(p + 1));
				written++;
				len -= 2;
				p += 2;
			}
			// Percent signs that aren't followed by a valid hex pair are invalid, but in the interest of compatibility we'll simply let
			// those characters through.
			else if (len >= 1) {
				*o++ = '%';
				*o++ = *p++;
				written += 2;
				len--;
			}
		}
		// Characters not prefixed by a percent sign are simply copied into the output buffer.
		else {
			*o++ = *p++;
			written++;
			len--;
		}

	}

	// We allocated a default string buffer, which means the length is tracked so we need to set the data length.
	st_length_set(output, written);
	return output;
}
示例#20
0
文件: replace.c 项目: lavabit/magma
 // LOW: Function could use some serious cleanup; NEED TO TEST THIS IN UNIT TESTS
int_t st_replace(stringer_t **target, stringer_t *pattern, stringer_t *replacement) {

	stringer_t *output;
	uchr_t *tptr, *optr, *rptr, *pptr;
	size_t hits = 0, tlen, plen, rlen, olen;

	// replacement can be blank but it can't be null
	if (!target || st_empty_out(*target, &tptr, &tlen) || st_empty_out(pattern, &pptr, &plen) ||
			(st_empty_out(replacement, &rptr, &rlen) && !st_char_get(replacement))) {
		log_pedantic("Sanity check failed. Passed a NULL pointer.");
		return -1;
	}

	// Check to make sure the target is big enough to hold the pattern.
	if (tlen < plen) {
//		log_pedantic("The target isn't long enough to contain the pattern.");
		return 0;
	}

	// Increment through the entire target and find out how many times the pattern is present.
	for (size_t i = 0; i <= (tlen - plen); i++) {
		if (!st_cmp_cs_starts(PLACER(tptr++, tlen - i), pattern)) {
			hits++;
			i += plen - 1;
			tptr += plen - 1;
		}
	}

	//  Did we get any hits? Or if the output length would be zero, return.
	// QUESTION: Should 2nd part of conditional ever be necessary? tlen - (plen * hits) can't be less than zero,
	//           and hits has to be positive. So the expression will always evaluate positive.
	// QUESTION: Shouldn't we allow the output length to be zero?
	/*if (!hits || !(olen = tlen - (plen * hits) + (rlen * hits))) {
		return hits;
	}*/

	if (!hits) {
		return 0;
	}

	olen = tlen - (plen * hits) + (rlen * hits);

	// If our new string is now empty we truncate the original target and return it.
	if (!olen) {
		*((char *)(st_data_get(*target))) = 0;
		st_length_set(*target, 0);
		return hits;
	}

	// Allocate a new stringer.
	if (!(output = st_alloc(olen))) {
		log_pedantic("Could not allocate %zu bytes for the new string.", olen);
		// QUESTION: -3?
		return -3;
	}

	// Setup.
	tptr = st_data_get(*target);
	optr = st_data_get(output);

	// Increment through the entire target and copy the bytes.
	for (size_t i = 0; i <= tlen; i++) {
		if (i <= (tlen - plen) && !st_cmp_cs_starts(PLACER(tptr, tlen - i), pattern)) {
			i += plen - 1;
			tptr += plen;
			for (size_t j = 0; j < rlen; j++) {
				*optr++ = *(rptr + j);
			}
		}
		else {
			*optr++ = *tptr++;
		}
	}

	if (st_valid_free(*((uint32_t *)(*target)))) {
		st_free(*target);
	}

	st_length_set(output, olen);
	*target = output;
	return hits;
}