Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F2822952
totemcrypto.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
18 KB
Referenced Files
None
Subscribers
None
totemcrypto.c
View Options
/*
* Copyright (c) 2006-2012 Red Hat, Inc.
*
* All rights reserved.
*
* Author: Steven Dake (sdake@redhat.com)
* Christine Caulfield (ccaulfie@redhat.com)
* Jan Friesse (jfriesse@redhat.com)
* Fabio M. Di Nitto (fdinitto@redhat.com)
*
* This software licensed under BSD license, the text of which follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the MontaVista Software, Inc. nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include <nss.h>
#include <pk11pub.h>
#include <pkcs11.h>
#include <prerror.h>
#include <blapit.h>
#include <hasht.h>
#define LOGSYS_UTILS_ONLY 1
#include <corosync/logsys.h>
#include <corosync/totem/totem.h>
#include "totemcrypto.h"
/*
* define onwire crypto header
*/
struct crypto_config_header {
uint8_t crypto_cipher_type;
uint8_t crypto_hash_type;
uint8_t __pad0;
uint8_t __pad1;
} __attribute__((packed));
/*
* crypto definitions and conversion tables
*/
#define SALT_SIZE 16
enum crypto_crypt_t {
CRYPTO_CIPHER_TYPE_NONE = 0,
CRYPTO_CIPHER_TYPE_AES256 = 1
};
CK_MECHANISM_TYPE cipher_to_nss[] = {
0, /* CRYPTO_CIPHER_TYPE_NONE */
CKM_AES_CBC_PAD /* CRYPTO_CIPHER_TYPE_AES256 */
};
size_t cipher_key_len[] = {
0, /* CRYPTO_CIPHER_TYPE_NONE */
32, /* CRYPTO_CIPHER_TYPE_AES256 */
};
size_t cypher_block_len[] = {
0, /* CRYPTO_CIPHER_TYPE_NONE */
AES_BLOCK_SIZE /* CRYPTO_CIPHER_TYPE_AES256 */
};
/*
* hash definitions and conversion tables
*/
enum crypto_hash_t {
CRYPTO_HASH_TYPE_NONE = 0,
CRYPTO_HASH_TYPE_MD5 = 1,
CRYPTO_HASH_TYPE_SHA1 = 2,
CRYPTO_HASH_TYPE_SHA256 = 3,
CRYPTO_HASH_TYPE_SHA384 = 4,
CRYPTO_HASH_TYPE_SHA512 = 5
};
CK_MECHANISM_TYPE hash_to_nss[] = {
0, /* CRYPTO_HASH_TYPE_NONE */
CKM_MD5_HMAC, /* CRYPTO_HASH_TYPE_MD5 */
CKM_SHA_1_HMAC, /* CRYPTO_HASH_TYPE_SHA1 */
CKM_SHA256_HMAC, /* CRYPTO_HASH_TYPE_SHA256 */
CKM_SHA384_HMAC, /* CRYPTO_HASH_TYPE_SHA384 */
CKM_SHA512_HMAC /* CRYPTO_HASH_TYPE_SHA512 */
};
size_t hash_len[] = {
0, /* CRYPTO_HASH_TYPE_NONE */
MD5_LENGTH, /* CRYPTO_HASH_TYPE_MD5 */
SHA1_LENGTH, /* CRYPTO_HASH_TYPE_SHA1 */
SHA256_LENGTH, /* CRYPTO_HASH_TYPE_SHA256 */
SHA384_LENGTH, /* CRYPTO_HASH_TYPE_SHA384 */
SHA512_LENGTH /* CRYPTO_HASH_TYPE_SHA512 */
};
size_t hash_block_len[] = {
0, /* CRYPTO_HASH_TYPE_NONE */
MD5_BLOCK_LENGTH, /* CRYPTO_HASH_TYPE_MD5 */
SHA1_BLOCK_LENGTH, /* CRYPTO_HASH_TYPE_SHA1 */
SHA256_BLOCK_LENGTH, /* CRYPTO_HASH_TYPE_SHA256 */
SHA384_BLOCK_LENGTH, /* CRYPTO_HASH_TYPE_SHA384 */
SHA512_BLOCK_LENGTH /* CRYPTO_HASH_TYPE_SHA512 */
};
struct crypto_instance {
PK11SymKey *nss_sym_key;
PK11SymKey *nss_sym_key_sign;
unsigned char private_key[1024];
unsigned int private_key_len;
enum crypto_crypt_t crypto_cipher_type;
enum crypto_hash_t crypto_hash_type;
unsigned int crypto_header_size;
void (*log_printf_func) (
int level,
int subsys,
const char *function,
const char *file,
int line,
const char *format,
...)__attribute__((format(printf, 6, 7)));
int log_level_security;
int log_level_notice;
int log_level_error;
int log_subsys_id;
};
#define log_printf(level, format, args...) \
do { \
instance->log_printf_func ( \
level, instance->log_subsys_id, \
__FUNCTION__, __FILE__, __LINE__, \
(const char *)format, ##args); \
} while (0);
/*
* crypt/decrypt functions
*/
static int string_to_crypto_cipher_type(const char* crypto_cipher_type)
{
if (strcmp(crypto_cipher_type, "none") == 0) {
return CRYPTO_CIPHER_TYPE_NONE;
} else if (strcmp(crypto_cipher_type, "aes256") == 0) {
return CRYPTO_CIPHER_TYPE_AES256;
}
return CRYPTO_CIPHER_TYPE_AES256;
}
static int init_nss_crypto(struct crypto_instance *instance)
{
PK11SlotInfo* crypt_slot = NULL;
SECItem crypt_param;
if (!cipher_to_nss[instance->crypto_cipher_type]) {
return 0;
}
crypt_param.type = siBuffer;
crypt_param.data = instance->private_key;
crypt_param.len = cipher_key_len[instance->crypto_cipher_type];
crypt_slot = PK11_GetBestSlot(cipher_to_nss[instance->crypto_cipher_type], NULL);
if (crypt_slot == NULL) {
log_printf(instance->log_level_security, "Unable to find security slot (err %d)",
PR_GetError());
return -1;
}
instance->nss_sym_key = PK11_ImportSymKey(crypt_slot,
cipher_to_nss[instance->crypto_cipher_type],
PK11_OriginUnwrap, CKA_ENCRYPT|CKA_DECRYPT,
&crypt_param, NULL);
if (instance->nss_sym_key == NULL) {
log_printf(instance->log_level_security, "Failure to import key into NSS (err %d)",
PR_GetError());
return -1;
}
PK11_FreeSlot(crypt_slot);
return 0;
}
static int encrypt_nss(
struct crypto_instance *instance,
const unsigned char *buf_in,
const size_t buf_in_len,
unsigned char *buf_out,
size_t *buf_out_len)
{
PK11Context* crypt_context = NULL;
SECItem crypt_param;
SECItem *nss_sec_param = NULL;
int tmp1_outlen = 0;
unsigned int tmp2_outlen = 0;
unsigned char *salt = buf_out;
unsigned char *data = buf_out + SALT_SIZE;
int err = -1;
if (!cipher_to_nss[instance->crypto_cipher_type]) {
memcpy(buf_out, buf_in, buf_in_len);
*buf_out_len = buf_in_len;
return 0;
}
if (PK11_GenerateRandom (salt, SALT_SIZE) != SECSuccess) {
log_printf(instance->log_level_security,
"Failure to generate a random number %d",
PR_GetError());
goto out;
}
crypt_param.type = siBuffer;
crypt_param.data = salt;
crypt_param.len = SALT_SIZE;
nss_sec_param = PK11_ParamFromIV (cipher_to_nss[instance->crypto_cipher_type],
&crypt_param);
if (nss_sec_param == NULL) {
log_printf(instance->log_level_security,
"Failure to set up PKCS11 param (err %d)",
PR_GetError());
goto out;
}
/*
* Create cipher context for encryption
*/
crypt_context = PK11_CreateContextBySymKey (cipher_to_nss[instance->crypto_cipher_type],
CKA_ENCRYPT,
instance->nss_sym_key,
nss_sec_param);
if (!crypt_context) {
log_printf(instance->log_level_security,
"PK11_CreateContext failed (encrypt) crypt_type=%d (err %d)",
(int)cipher_to_nss[instance->crypto_cipher_type],
PR_GetError());
goto out;
}
if (PK11_CipherOp(crypt_context, data,
&tmp1_outlen,
FRAME_SIZE_MAX - instance->crypto_header_size,
(unsigned char *)buf_in, buf_in_len) != SECSuccess) {
log_printf(instance->log_level_security,
"PK11_CipherOp failed (encrypt) crypt_type=%d (err %d)",
(int)cipher_to_nss[instance->crypto_cipher_type],
PR_GetError());
goto out;
}
if (PK11_DigestFinal(crypt_context, data + tmp1_outlen,
&tmp2_outlen, FRAME_SIZE_MAX - tmp1_outlen) != SECSuccess) {
log_printf(instance->log_level_security,
"PK11_DigestFinal failed (encrypt) crypt_type=%d (err %d)",
(int)cipher_to_nss[instance->crypto_cipher_type],
PR_GetError());
goto out;
}
*buf_out_len = tmp1_outlen + tmp2_outlen + SALT_SIZE;
err = 0;
out:
if (crypt_context) {
PK11_DestroyContext(crypt_context, PR_TRUE);
}
if (nss_sec_param) {
SECITEM_FreeItem(nss_sec_param, PR_TRUE);
}
return err;
}
static int decrypt_nss (
struct crypto_instance *instance,
unsigned char *buf,
int *buf_len)
{
PK11Context* decrypt_context = NULL;
SECItem decrypt_param;
int tmp1_outlen = 0;
unsigned int tmp2_outlen = 0;
unsigned char *salt = buf;
unsigned char *data = salt + SALT_SIZE;
int datalen = *buf_len - SALT_SIZE;
unsigned char outbuf[FRAME_SIZE_MAX];
int outbuf_len;
int err = -1;
if (!cipher_to_nss[instance->crypto_cipher_type]) {
return 0;
}
/* Create cipher context for decryption */
decrypt_param.type = siBuffer;
decrypt_param.data = salt;
decrypt_param.len = SALT_SIZE;
decrypt_context = PK11_CreateContextBySymKey(cipher_to_nss[instance->crypto_cipher_type],
CKA_DECRYPT,
instance->nss_sym_key, &decrypt_param);
if (!decrypt_context) {
log_printf(instance->log_level_security,
"PK11_CreateContext (decrypt) failed (err %d)",
PR_GetError());
goto out;
}
if (PK11_CipherOp(decrypt_context, outbuf, &tmp1_outlen,
sizeof(outbuf), data, datalen) != SECSuccess) {
log_printf(instance->log_level_security,
"PK11_CipherOp (decrypt) failed (err %d)",
PR_GetError());
goto out;
}
if (PK11_DigestFinal(decrypt_context, outbuf + tmp1_outlen, &tmp2_outlen,
sizeof(outbuf) - tmp1_outlen) != SECSuccess) {
log_printf(instance->log_level_security,
"PK11_DigestFinal (decrypt) failed (err %d)",
PR_GetError());
goto out;
}
outbuf_len = tmp1_outlen + tmp2_outlen;
memset(buf, 0, *buf_len);
memcpy(buf, outbuf, outbuf_len);
*buf_len = outbuf_len;
err = 0;
out:
if (decrypt_context) {
PK11_DestroyContext(decrypt_context, PR_TRUE);
}
return err;
}
/*
* hash/hmac/digest functions
*/
static int string_to_crypto_hash_type(const char* crypto_hash_type)
{
if (strcmp(crypto_hash_type, "none") == 0) {
return CRYPTO_HASH_TYPE_NONE;
} else if (strcmp(crypto_hash_type, "md5") == 0) {
return CRYPTO_HASH_TYPE_MD5;
} else if (strcmp(crypto_hash_type, "sha1") == 0) {
return CRYPTO_HASH_TYPE_SHA1;
} else if (strcmp(crypto_hash_type, "sha256") == 0) {
return CRYPTO_HASH_TYPE_SHA256;
} else if (strcmp(crypto_hash_type, "sha384") == 0) {
return CRYPTO_HASH_TYPE_SHA384;
} else if (strcmp(crypto_hash_type, "sha512") == 0) {
return CRYPTO_HASH_TYPE_SHA512;
}
return CRYPTO_HASH_TYPE_SHA1;
}
static int init_nss_hash(struct crypto_instance *instance)
{
PK11SlotInfo* hash_slot = NULL;
SECItem hash_param;
if (!hash_to_nss[instance->crypto_hash_type]) {
return 0;
}
hash_param.type = siBuffer;
hash_param.data = 0;
hash_param.len = 0;
hash_slot = PK11_GetBestSlot(hash_to_nss[instance->crypto_hash_type], NULL);
if (hash_slot == NULL) {
log_printf(instance->log_level_security, "Unable to find security slot (err %d)",
PR_GetError());
return -1;
}
instance->nss_sym_key_sign = PK11_ImportSymKey(hash_slot,
hash_to_nss[instance->crypto_hash_type],
PK11_OriginUnwrap, CKA_SIGN,
&hash_param, NULL);
if (instance->nss_sym_key_sign == NULL) {
log_printf(instance->log_level_security, "Failure to import key into NSS (err %d)",
PR_GetError());
return -1;
}
PK11_FreeSlot(hash_slot);
return 0;
}
static int calculate_nss_hash(
struct crypto_instance *instance,
const unsigned char *buf,
const size_t buf_len,
unsigned char *hash)
{
PK11Context* hash_context = NULL;
SECItem hash_param;
unsigned int hash_tmp_outlen = 0;
unsigned char hash_block[hash_block_len[instance->crypto_hash_type]];
int err = -1;
/* Now do the digest */
hash_param.type = siBuffer;
hash_param.data = 0;
hash_param.len = 0;
hash_context = PK11_CreateContextBySymKey(hash_to_nss[instance->crypto_hash_type],
CKA_SIGN,
instance->nss_sym_key_sign,
&hash_param);
if (!hash_context) {
log_printf(instance->log_level_security,
"PK11_CreateContext failed (hash) hash_type=%d (err %d)",
(int)hash_to_nss[instance->crypto_hash_type],
PR_GetError());
goto out;
}
if (PK11_DigestBegin(hash_context) != SECSuccess) {
log_printf(instance->log_level_security,
"PK11_DigestBegin failed (hash) hash_type=%d (err %d)",
(int)hash_to_nss[instance->crypto_hash_type],
PR_GetError());
goto out;
}
if (PK11_DigestOp(hash_context,
buf,
buf_len) != SECSuccess) {
log_printf(instance->log_level_security,
"PK11_DigestOp failed (hash) hash_type=%d (err %d)",
(int)hash_to_nss[instance->crypto_hash_type],
PR_GetError());
goto out;
}
if (PK11_DigestFinal(hash_context,
hash_block,
&hash_tmp_outlen,
hash_block_len[instance->crypto_hash_type]) != SECSuccess) {
log_printf(instance->log_level_security,
"PK11_DigestFinale failed (hash) hash_type=%d (err %d)",
(int)hash_to_nss[instance->crypto_hash_type],
PR_GetError());
goto out;
}
memcpy(hash, hash_block, hash_len[instance->crypto_hash_type]);
err = 0;
out:
if (hash_context) {
PK11_DestroyContext(hash_context, PR_TRUE);
}
return err;
}
/*
* global/glue nss functions
*/
static int init_nss_db(struct crypto_instance *instance)
{
if ((!cipher_to_nss[instance->crypto_cipher_type]) &&
(!hash_to_nss[instance->crypto_hash_type])) {
return 0;
}
if (NSS_NoDB_Init(".") != SECSuccess) {
log_printf(instance->log_level_security, "NSS DB initialization failed (err %d)",
PR_GetError());
return -1;
}
return 0;
}
static int init_nss(struct crypto_instance *instance,
const char *crypto_cipher_type,
const char *crypto_hash_type)
{
log_printf(instance->log_level_notice,
"Initializing transmit/receive security (NSS) crypto: %s hash: %s",
crypto_cipher_type, crypto_hash_type);
if (init_nss_db(instance) < 0) {
return -1;
}
if (init_nss_crypto(instance) < 0) {
return -1;
}
if (init_nss_hash(instance) < 0) {
return -1;
}
return 0;
}
static int encrypt_and_sign_nss (
struct crypto_instance *instance,
const unsigned char *buf_in,
const size_t buf_in_len,
unsigned char *buf_out,
size_t *buf_out_len)
{
unsigned char *hash = buf_out;
unsigned char *data = hash + hash_len[instance->crypto_hash_type];
if (encrypt_nss(instance, buf_in, buf_in_len, data, buf_out_len) < 0) {
return -1;
}
if (hash_to_nss[instance->crypto_hash_type]) {
if (calculate_nss_hash(instance, data, *buf_out_len, hash) < 0) {
return -1;
}
*buf_out_len = *buf_out_len + hash_len[instance->crypto_hash_type];
}
return 0;
}
static int authenticate_and_decrypt_nss (
struct crypto_instance *instance,
unsigned char *buf,
int *buf_len)
{
if (hash_to_nss[instance->crypto_hash_type]) {
unsigned char tmp_hash[hash_len[instance->crypto_hash_type]];
unsigned char *hash = buf;
unsigned char *data = hash + hash_len[instance->crypto_hash_type];
int datalen = *buf_len - hash_len[instance->crypto_hash_type];
if (calculate_nss_hash(instance, data, datalen, tmp_hash) < 0) {
return -1;
}
if (memcmp(tmp_hash, hash, hash_len[instance->crypto_hash_type]) != 0) {
log_printf(instance->log_level_error, "Digest does not match");
return -1;
}
memmove(buf, data, datalen);
*buf_len = datalen;
}
if (decrypt_nss(instance, buf, buf_len) < 0) {
return -1;
}
return 0;
}
/*
* exported API
*/
size_t crypto_sec_header_size(
const char *crypto_cipher_type,
const char *crypto_hash_type)
{
int crypto_cipher = string_to_crypto_cipher_type(crypto_cipher_type);
int crypto_hash = string_to_crypto_hash_type(crypto_hash_type);
size_t hdr_size = 0;
hdr_size = sizeof(struct crypto_config_header);
if (crypto_hash) {
hdr_size += hash_len[crypto_hash];
}
if (crypto_cipher) {
hdr_size += SALT_SIZE;
hdr_size += cypher_block_len[crypto_cipher];
}
return hdr_size;
}
int crypto_encrypt_and_sign (
struct crypto_instance *instance,
const unsigned char *buf_in,
const size_t buf_in_len,
unsigned char *buf_out,
size_t *buf_out_len)
{
struct crypto_config_header *cch = (struct crypto_config_header *)buf_out;
int err;
cch->crypto_cipher_type = instance->crypto_cipher_type;
cch->crypto_hash_type = instance->crypto_hash_type;
cch->__pad0 = 0;
cch->__pad1 = 0;
buf_out += sizeof(struct crypto_config_header);
err = encrypt_and_sign_nss(instance,
buf_in, buf_in_len,
buf_out, buf_out_len);
*buf_out_len = *buf_out_len + sizeof(struct crypto_config_header);
return err;
}
int crypto_authenticate_and_decrypt (struct crypto_instance *instance,
unsigned char *buf,
int *buf_len)
{
struct crypto_config_header *cch = (struct crypto_config_header *)buf;
/*
* decode crypto config of incoming packets
*/
if (cch->crypto_cipher_type != instance->crypto_cipher_type) {
log_printf(instance->log_level_security,
"Incoming packet has different crypto type. Rejecting");
return -1;
}
if (cch->crypto_hash_type != instance->crypto_hash_type) {
log_printf(instance->log_level_security,
"Incoming packet has different hash type. Rejecting");
return -1;
}
if ((cch->__pad0 != 0) || (cch->__pad1 != 0)) {
log_printf(instance->log_level_security,
"Incoming packet appears to have features not supported by this version of corosync. Rejecting");
return -1;
}
/*
* invalidate config header and kill it
*/
cch = NULL;
*buf_len -= sizeof(struct crypto_config_header);
memmove(buf, buf + sizeof(struct crypto_config_header), *buf_len);
return authenticate_and_decrypt_nss(instance, buf, buf_len);
}
struct crypto_instance *crypto_init(
const unsigned char *private_key,
unsigned int private_key_len,
const char *crypto_cipher_type,
const char *crypto_hash_type,
void (*log_printf_func) (
int level,
int subsys,
const char *function,
const char *file,
int line,
const char *format,
...)__attribute__((format(printf, 6, 7))),
int log_level_security,
int log_level_notice,
int log_level_error,
int log_subsys_id)
{
struct crypto_instance *instance;
instance = malloc(sizeof(*instance));
if (instance == NULL) {
return (NULL);
}
memset(instance, 0, sizeof(struct crypto_instance));
memcpy(instance->private_key, private_key, private_key_len);
instance->private_key_len = private_key_len;
instance->crypto_cipher_type = string_to_crypto_cipher_type(crypto_cipher_type);
instance->crypto_hash_type = string_to_crypto_hash_type(crypto_hash_type);
instance->crypto_header_size = crypto_sec_header_size(crypto_cipher_type, crypto_hash_type);
instance->log_printf_func = log_printf_func;
instance->log_level_security = log_level_security;
instance->log_level_notice = log_level_notice;
instance->log_level_error = log_level_error;
instance->log_subsys_id = log_subsys_id;
if (init_nss(instance, crypto_cipher_type, crypto_hash_type) < 0) {
free(instance);
return(NULL);
}
return (instance);
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, Jan 25, 6:56 AM (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1314284
Default Alt Text
totemcrypto.c (18 KB)
Attached To
Mode
rC Corosync
Attached
Detach File
Event Timeline
Log In to Comment