Пример #1
0
/**
 * @brief	Allocate a new ECIES key pair from the curve defined by ECIES_CURVE.
 * @see		NID_sect571k1
 * @return	NULL on failure, or a pointer to the newly allocated key pair.
 */
EC_KEY * deprecated_ecies_key_alloc(void) {

	EC_KEY *key = NULL;

	// Create a key and assign the group.
	if (ecies_curve_group) {

		if (!(key = EC_KEY_new_d())) {
			log_info("An error occurred while initializing an empty ECIES key context. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		} else if (EC_KEY_set_group_d(key, ecies_curve_group) != 1) {
			log_info("Unable to assign the default group to our empty key context.. {%s}", ssl_error_string(MEMORYBUF(256), 256));
			EC_KEY_free_d(key);
			key = NULL;
		}

	}

	// If an error occurs above, attempt key creation from scratch.
	if (!key) {

		if (!(key = EC_KEY_new_by_curve_name_d(ECIES_CURVE))) {
			log_info("An error occurred while trying to create a new ECIES key using the default curve. {%s}", ssl_error_string(MEMORYBUF(256), 256));
			return NULL;
		}

		EC_KEY_set_conv_form_d(key, POINT_CONVERSION_COMPRESSED);
	}

	return key;
}
Пример #2
0
/**
 * @brief	Return an ECIES public key as binary data.
 * @param	key		the input ECIES key pair.
 * @param	olen	a pointer to store the length of the returned key.
 * @return	NULL on failure, or a pointer to the raw public key.
 */
uchr_t * deprecated_ecies_key_public_bin(EC_KEY *key, size_t *olen) {

	uchr_t *result;
	size_t rlen, blen = 512;
	const EC_POINT *point;
	const EC_GROUP *group;

	if (!(point = EC_KEY_get0_public_key_d(key))) {
		log_info("No public key available. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		return NULL;
	} else if (!(group = EC_KEY_get0_group_d(key))) {
		log_info("No group available. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		return NULL;
	} else if (!(result = mm_alloc(blen))) {
		log_info("Error allocating space for ECIES public key.");
		return NULL;
	} else if ((rlen = EC_POINT_point2oct_d(group, point, POINT_CONVERSION_COMPRESSED, result, blen, NULL)) <= 0) {
		log_info("Unable to extract the public key. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		mm_free(result);
		return NULL;
	}

	if (olen) {
		*olen = rlen;
	}

	//	char * (*EC_POINT_point2hex_d)(const EC_GROUP *, const EC_POINT *, point_conversion_form_t form, BN_CTX *) __attribute__ ((common)) = NULL;
	//size_t (*EC_POINT_point2oct_d)(const EC_GROUP *, const EC_POINT *, point_conversion_form_t form, unsigned char *buf, size_t len, BN_CTX *ctx) __attribute__ ((common)) = NULL;

	return result;
}
Пример #3
0
/**
 * @brief	Return an ECIES private key as a hex string.
 * @param	key	the input ECIES key pair.
 * @return	NULL on failure, or the hex-formatted private key as a managed string.
 */
stringer_t * deprecated_ecies_key_private_hex(EC_KEY *key) {

	chr_t *hex;
	const BIGNUM *bn;
	stringer_t *result = NULL;

	if (!(bn = EC_KEY_get0_private_key_d(key))) {
		log_pedantic("No private key available. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		return NULL;
	}
	else if (!(hex = BN_bn2hex_d(bn))) {
		log_pedantic("Unable to serialize the private key into hex. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		return NULL;
	}

	else if (!(result = st_import_opts(MANAGED_T | CONTIGUOUS | SECURE, hex, ns_length_get(hex) + 1))) {
		log_pedantic("Unable to allocate secure buffer for hex string.");
		ns_wipe(hex, ns_length_get(hex));
		OPENSSL_free_d(hex);
		return NULL;
	}

	ns_wipe(hex, ns_length_get(hex));
	OPENSSL_free_d(hex);

	return result;
}
Пример #4
0
/**
 * @brief	Return an ECIES public key as a null-terminated hex string.
 * @param	key	the input ECIES key pair.
 * @return	NULL on failure, or the hex-formatted public key as a null-terminated string.
 */
stringer_t * deprecated_ecies_key_public_hex(EC_KEY *key) {

	char *hex;
	const EC_POINT *point;
	const EC_GROUP *group;
	stringer_t *result = NULL;

	if (!(point = EC_KEY_get0_public_key_d(key))) {
		log_info("No public key available. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		return NULL;
	} else if (!(group = EC_KEY_get0_group_d(key))) {
		log_info("No group available. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		return NULL;
	} else if (!(hex = EC_POINT_point2hex_d(group, point, POINT_CONVERSION_COMPRESSED, NULL))) {
		log_info("Unable to serialize the public key into hex. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		return NULL;
	}

	if (!(result = st_import(hex, ns_length_get(hex) + 1))) {
		log_info("Unable to make copy of ECIES public key.");
	}

	OPENSSL_free_d(hex);

	return result;
}
Пример #5
0
/* Performs an SSL_read and handle errors. */
static tsi_result do_ssl_read(SSL* ssl, unsigned char* unprotected_bytes,
                              size_t* unprotected_bytes_size) {
  int read_from_ssl = SSL_read(ssl, unprotected_bytes, *unprotected_bytes_size);
  if (read_from_ssl == 0) {
    gpr_log(GPR_ERROR, "SSL_read returned 0 unexpectedly.");
    return TSI_INTERNAL_ERROR;
  }
  if (read_from_ssl < 0) {
    read_from_ssl = SSL_get_error(ssl, read_from_ssl);
    switch (read_from_ssl) {
      case SSL_ERROR_WANT_READ:
        /* We need more data to finish the frame. */
        *unprotected_bytes_size = 0;
        return TSI_OK;
      case SSL_ERROR_WANT_WRITE:
        gpr_log(
            GPR_ERROR,
            "Peer tried to renegotiate SSL connection. This is unsupported.");
        return TSI_UNIMPLEMENTED;
      case SSL_ERROR_SSL:
        gpr_log(GPR_ERROR, "Corruption detected.");
        log_ssl_error_stack();
        return TSI_DATA_CORRUPTED;
      default:
        gpr_log(GPR_ERROR, "SSL_read failed with error %s.",
                ssl_error_string(read_from_ssl));
        return TSI_PROTOCOL_FAILURE;
    }
  }
  *unprotected_bytes_size = read_from_ssl;
  return TSI_OK;
}
Пример #6
0
/**
 * @brief	Return an ECIES private key as binary data.
 * @param	key		the input ECIES key pair.
 * @param	olen	a pointer to store the length of the returned key.
 * @return	NULL on failure, or a pointer to the raw private key.
 */
uchr_t * deprecated_ecies_key_private_bin(EC_KEY *key, size_t *olen) {

	const BIGNUM *bn;
	int bn_len;
	uchr_t *result;

	if (!(bn = EC_KEY_get0_private_key_d(key))) {
		log_info("No private key available. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		return NULL;
	} else if (!(bn_len = BN_num_bytes_d(bn))) {
		log_info("Error determining size of ECIES private key.");
		return NULL;
	}

	if (!(result = mm_sec_alloc(bn_len))) {
		log_info("Error allocating space for ECIES private key.");
		return NULL;
	}
	else if (!BN_bn2bin_d(bn, (unsigned char *)result)) {
		log_info("Error retrieving ECIES private key.");
		mm_sec_free(result);
		return NULL;
	}

	if (olen) {
		*olen = bn_len;
	}

	return result;
}
Пример #7
0
EC_GROUP * deprecated_ecies_group(uint64_t curve, bool_t precompute) {

	EC_GROUP *group;

	if (!(group = EC_GROUP_new_by_curve_name_d(curve))) {
		log_error("An error occurred while trying to create the elliptical group. {%s}",
				ssl_error_string(MEMORYBUF(256), 256));
		return NULL;
	} else if (precompute && EC_GROUP_precompute_mult_d(group, NULL) != 1) {
		log_error("Unable to precompute the required elliptical curve point data. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		EC_GROUP_free_d(group);
		return NULL;
	}

	EC_GROUP_set_point_conversion_form_d(group, POINT_CONVERSION_COMPRESSED);

	return group;
}
Пример #8
0
EC_KEY * deprecated_ecies_key_private(uint64_t format, placer_t data) {

	EC_KEY *key = NULL;
	BIGNUM *number = NULL;

	if (!(key = deprecated_ecies_key_alloc())) {
		log_info("Unable to allocate an empty key context.");
		return NULL;
	}

	// Process a key in binary format.
	if (format & ECIES_PRIVATE_BINARY) {

		if (!(number = BN_bin2bn_d(pl_data_get(data), pl_length_get(data), NULL))) {
			log_info("An error occurred while parsing the binary elliptical curve point data used to represent the private key. {%s}", ssl_error_string(MEMORYBUF(256), 256));
			EC_KEY_free_d(key);
			return NULL;
		}

	}
	// Process a key in hex.
	else if (format & ECIES_PRIVATE_HEX) {

		if (!(BN_hex2bn_d(&number, pl_char_get(data)))) {
			log_info("An error occurred while parsing the binary elliptical curve point data used to represent the private key. {%s}", ssl_error_string(MEMORYBUF(256), 256));
			EC_KEY_free_d(key);
			return NULL;
		}

	}
	// Invalid format!
	else {
		log_info("The private key data is using an unrecognized format.");
		EC_KEY_free_d(key);
		return NULL;
	}

	// Assign the point to our empty key instance.
	if (EC_KEY_set_private_key_d(key, number) != 1) {
		log_info("The provided point data does not represent a valid public key. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		EC_KEY_free_d(key);
		BN_free_d(number);
		return NULL;
	}

	// The above function call duplicates the point so the local copy is no longer needed.
	BN_free_d(number);

	return key;
}
Пример #9
0
/* Performs an SSL_write and handle errors. */
static tsi_result do_ssl_write(SSL* ssl, unsigned char* unprotected_bytes,
                               size_t unprotected_bytes_size) {
  int ssl_write_result =
      SSL_write(ssl, unprotected_bytes, unprotected_bytes_size);
  if (ssl_write_result < 0) {
    ssl_write_result = SSL_get_error(ssl, ssl_write_result);
    if (ssl_write_result == SSL_ERROR_WANT_READ) {
      gpr_log(GPR_ERROR,
              "Peer tried to renegotiate SSL connection. This is unsupported.");
      return TSI_UNIMPLEMENTED;
    } else {
      gpr_log(GPR_ERROR, "SSL_write failed with error %s.",
              ssl_error_string(ssl_write_result));
      return TSI_INTERNAL_ERROR;
    }
  }
  return TSI_OK;
}
Пример #10
0
/**
 * @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;
}
Пример #11
0
/**
 * @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;
}
Пример #12
0
EC_KEY * deprecated_ecies_key_public(uint64_t format, placer_t data) {

	EC_KEY *key = NULL;
	EC_POINT *point = NULL;

	if (!(key = deprecated_ecies_key_alloc())) {
		log_info("Unable to allocate an empty key context.");
		return NULL;
	}

	// Process a key in binary format.
	if (format & ECIES_PUBLIC_BINARY) {

		// Generate the empty point context we'll be assigning to below.
		if (!(point = EC_POINT_new_d(EC_KEY_get0_group_d(key)))) {
			log_info("An error occurred while allocate the elliptical curve point. {%s}", ssl_error_string(MEMORYBUF(256), 256));
			EC_KEY_free_d(key);
			return NULL;
		}
		else if (EC_POINT_oct2point_d(EC_KEY_get0_group_d(key), point, pl_data_get(data), pl_length_get(data), NULL) != 1) {
			log_info("An error occurred while parsing the binary elliptical curve point data used to represent the public key. {%s}",
					ssl_error_string(MEMORYBUF(256), 256));
			EC_POINT_free_d(point);
			EC_KEY_free_d(key);
			return NULL;
		}

	}
	// Process a key in hex.
	else if (format & ECIES_PUBLIC_HEX) {

		if (!(point = EC_POINT_hex2point_d(EC_KEY_get0_group_d(key), pl_char_get(data), NULL, NULL))) {
			log_info("An error occurred while parsing the binary elliptical curve point data used to represent the public key. {%s}",
					ssl_error_string(MEMORYBUF(256), 256));
			EC_KEY_free_d(key);
			return NULL;
		}

	}
	// Invalid format!
	else {
		log_info("The public key data is using an unrecognized format.");
		EC_POINT_free_d(point);
		EC_KEY_free_d(key);
		return NULL;
	}

	// Assign the point to our empty key instance.
	if (EC_KEY_set_public_key_d(key, point) != 1) {
		log_info("The provided point data does not represent a valid public key. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		EC_POINT_free_d(point);
		EC_KEY_free_d(key);
		return NULL;
	}

	// The above function call duplicates the point so the local copy is no longer needed.
	EC_POINT_free_d(point);

	// Ensures the provided point is along the active elliptical curve and therefore represents a valid public key.
	if (EC_KEY_check_key_d(key) != 1) {
		log_info("The provided point data does not represent a valid public key. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		EC_KEY_free_d(key);
		return NULL;
	}

	return key;
}
Пример #13
0
/**
 * @brief	Generate a random ECIES key pair.
 * @return	NULL on failure, or a new random ECIES key pair on success.
 */
EC_KEY * deprecated_ecies_key_create(void) {

	EC_KEY *key = NULL;

	if (!(key = deprecated_ecies_key_alloc())) {
		log_info("Unable to allocate an empty key context.");
		return NULL;
	}

	// This should generate a random key pair.
	if (EC_KEY_generate_key_d(key) != 1) {
		log_info("An error occurred while trying to generate a random ECIES key pair. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		EC_KEY_free_d(key);
		return NULL;
	}

	return key;
}
Пример #14
0
stringer_t * hash_digest(digest_t *digest, stringer_t *s, stringer_t *output) {

	int_t olen;
	uint_t rlen;
	uint32_t opts = 0;
	EVP_MD_CTX ctx;
	stringer_t *result = NULL;

	// Ensure a digest pointer was passed in and that we can retrieve the output length.
	if (!digest || (olen = EVP_MD_size_d((const EVP_MD *)digest)) <= 0) {
		log_pedantic("The hash algorithm is missing or invalid.");
		return NULL;
	}
	else 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 a result.");
		return NULL;
	}
	else if (st_empty(s)) {
		log_pedantic("The input string does not appear to have any data ready for encoding. {%slen = %zu}", s ? "" : "s = NULL / ",	s ? st_length_get(s) : 0);
		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.
	else if ((result = output) && ((st_valid_avail(opts) && st_avail_get(output) < olen) || (!st_valid_avail(opts) && st_length_get(output) < olen))) {
		log_pedantic("The output buffer supplied is not large enough to hold the result. {avail = %zu / required = %i}",
				st_valid_avail(opts) ? st_avail_get(output) : st_length_get(output), olen);
		return NULL;
	}
	else if (!output && !(result = st_alloc(olen))) {
		log_pedantic("The output buffer memory allocation request failed. {requested = %i}", olen);
		return NULL;
	}

	// Initialize the context.
	EVP_MD_CTX_init_d(&ctx);
	rlen = olen;

	// Setup the digest algorithm.
	if (EVP_DigestInit_ex_d(&ctx, (const EVP_MD *)digest, NULL) != 1) {
		log_pedantic("An error occurred while trying to initialize the hash context. {%s}",	ssl_error_string(MEMORYBUF(256), 256));
		EVP_MD_CTX_cleanup_d(&ctx);
		if (!output) {
			st_free(result);
		}
		return NULL;
	}

	// Process the input data.
	else if (EVP_DigestUpdate_d(&ctx, st_data_get(s), st_length_get(s)) != 1) {
		log_pedantic("An error occurred while trying to process the input data. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		EVP_MD_CTX_cleanup_d(&ctx);
		if (!output) {
			st_free(result);
		}
		return NULL;
	}

	// Retrieve the hash output.
	else if (EVP_DigestFinal_d(&ctx, st_data_get(result), &rlen) != 1) {
		log_pedantic("An error occurred while trying to retrieve the hash result. {%s}", ssl_error_string(MEMORYBUF(256), 256));
		EVP_MD_CTX_cleanup_d(&ctx);
		if (!output) {
			st_free(result);
		}
		return NULL;
	}

	// Cleanup.
	EVP_MD_CTX_cleanup_d(&ctx);

	if (!output || st_valid_tracked(opts)) {
		st_length_set(result, rlen);
	}
	return result;
}
Пример #15
0
/**
 * @brief	Establish an TLS client wrapper around a socket descriptor.
 * @param	sockd	the file descriptor of the socket to have its transport security level upgraded.
 * @return	NULL on failure or a pointer to the SSL handle of the file descriptor if SSL negotiation was successful.
 */
void * tls_client_alloc(int_t sockd) {

	BIO *bio;
	SSL *tls;
	int_t result = 0, counter = 0;
	SSL_CTX *ctx = NULL;
	long options = (SSL_OP_ALL | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_MODE_AUTO_RETRY);

	// Clear the error state, so we get accurate indications of a problem.
	errno = 0;
	ERR_clear_error_d();

	if (!(ctx = SSL_CTX_new_d(SSLv23_client_method_d()))) {
		log_pedantic("Could not create a valid TLS context. { error = %s }", ssl_error_string(MEMORYBUF(512), 512));
		return NULL;
	}
	else if ((SSL_CTX_ctrl_d(ctx, SSL_CTRL_OPTIONS, options, NULL) & options) != options) {
		log_pedantic("Could set the options mask on the TLS context. { error = %s }", ssl_error_string(MEMORYBUF(512), 512));
		SSL_CTX_free_d(ctx);
		return NULL;
	}

	/// LOW: Add requisite config options and sandbox resources to verify server TLS certificates.
	// SSL_CTX_load_verify_locations(result, SSL_CAFILE, SSL_CAPATH);
	// lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(result.context), X509_LOOKUP_file());
	// X509_load_crl_file(lookup, SSL_CRLFILE, X509_FILETYPE_PEM);
	// X509_STORE_set_flags(SSL_CTX_get_cert_store(result.context), X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
	// mode = SSL_VERIFY_NONE or SSL_VERIFY_PEER
	// SSL_CTX_set_verify(result.context, mode, cert_verify_callback);

	// We don't bother with server certificate verification, just yet.
	SSL_CTX_set_verify_d(ctx, SSL_VERIFY_NONE, NULL);

	if (!(tls = SSL_new_d(ctx))) {
		log_pedantic("Could create the TLS client connection context. { error = %s }", ssl_error_string(MEMORYBUF(512), 512));
		SSL_CTX_free_d(ctx);
		return NULL;
	}
	else if (!(bio = BIO_new_socket_d(sockd, BIO_NOCLOSE))) {
		log_pedantic("Could not create the TLS client BIO context. { error = %s }", ssl_error_string(MEMORYBUF(512), 512));
		SSL_CTX_free_d(ctx);
		SSL_free_d(tls);
		return NULL;
	}

	SSL_set_bio_d(tls, bio, bio);
	SSL_set_connect_state_d(tls);

	SSL_CTX_free_d(ctx);

	do {

		// Attempt the connection. Retry if the error indicates a retryable error.
		if ((result = SSL_connect_d(tls)) < 0) {
			log_pedantic("Could not establish a TLS client connection, but the result indicates we should retry. { error = %i }", SSL_get_error_d(tls, result));
		}

		else if (result < 1) {
			log_pedantic("Could not establish a TLS client connection. { error = %s }", ssl_error_string(MEMORYBUF(512), 512));
		}

	} while (result < 0 && counter++ < 10);

	if (result != 1) {
		SSL_free_d(tls);
		return NULL;
	}

	return tls;
}
Пример #16
0
/**
 * @brief	Create a TLS session for a file descriptor, and accept the client TLS/SSL handshake.
 * @see		SSL_accept()
 * @see		BIO_new_socket()
 * @param	server	a server object which contains the underlying SSL context.
 * @param	sockd	the file descriptor of the TCP connection to be made SSL-ready.
 * @param	flags	passed to BIO_new_socket(), determines whether the socket is shut down when the BIO is freed.
 */
TLS * tls_server_alloc(void *server, int sockd, int flags) {

	SSL *tls;
	BIO *bio;
	server_t *local = server;
	int_t error = 0, result = 0, counter = 0;

	// Clear the error state, so we get accurate indications of a problem.
	errno = 0;
	ERR_clear_error_d();

#ifdef MAGMA_PEDANTIC
	if (!local) {
		log_pedantic("Passed a NULL server pointer.");
	}
	else if (!local->tls.context) {
		log_pedantic("Passed a NULL SSL context pointer.");
	}
	else if (sockd < 0) {
		log_pedantic("Passed an invalid socket. { sockd = %i }", sockd);
	}
#endif

	if (!local || !local->tls.context || sockd < 0) {
		return NULL;
	}
	else if (!(tls = SSL_new_d(local->tls.context)) || !(bio = BIO_new_socket_d(sockd, flags))) {
		log_pedantic("TLS/BIO allocation error. { error = %s }", ssl_error_string(MEMORYBUF(256), 256));

		if (tls) {
			SSL_free_d(tls);
		}

		return NULL;
	}

	SSL_set_bio_d(tls, bio, bio);
	SSL_set_accept_state_d(tls);

	// If the result code indicates a handshake error, but the TCP connection is still alive, we retry the handshake.
	do {
		// Attempt the server connection setup.
		if ((result = SSL_accept_d(tls)) <= 0 && status()) {

			switch ((error = SSL_get_error_d(tls, result))) {

				// Log these errors with extra information.
				case (SSL_ERROR_SSL):
					log_pedantic("TLS accept error. { accept = %i / error = SSL_ERROR_SSL, message = %s }", result,
						ssl_error_string(MEMORYBUF(512), 512));
					break;
				case (SSL_ERROR_SYSCALL):
					log_pedantic("TLS accept error. { accept = %i / error = SSL_ERROR_SYSCALL / errno = %i / message = %s }", result,
						errno, errno_name(errno));
					break;

				// A zero return indicates a socket shutdown. The latter should never happen.
				case (SSL_ERROR_ZERO_RETURN):
				case (SSL_ERROR_NONE):
					break;

				default:
					log_pedantic("TLS accept error. { accept = %i / error = %i }", result, error);
					break;
			}
		}

	} while (result < 0 && counter++ < 10);

	if (result != 1) {
		SSL_free_d(tls);
		return NULL;
	}

	return tls;
}