/* * 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 #include #include #include #include #include #define LOGSYS_UTILS_ONLY 1 #include #include #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 /* * This are defined in new NSS. For older one, we will define our own */ #ifndef AES_256_KEY_LENGTH #define AES_256_KEY_LENGTH 32 #endif #ifndef AES_192_KEY_LENGTH #define AES_192_KEY_LENGTH 24 #endif #ifndef AES_128_KEY_LENGTH #define AES_128_KEY_LENGTH 16 #endif /* * while CRYPTO_CIPHER_TYPE_2_X are not a real cipher at all, * we still allocate a value for them because we use crypto_crypt_t * internally and we don't want overlaps */ enum crypto_crypt_t { CRYPTO_CIPHER_TYPE_NONE = 0, CRYPTO_CIPHER_TYPE_AES256 = 1, CRYPTO_CIPHER_TYPE_AES192 = 2, CRYPTO_CIPHER_TYPE_AES128 = 3, CRYPTO_CIPHER_TYPE_3DES = 4, CRYPTO_CIPHER_TYPE_2_3 = UINT8_MAX - 1, CRYPTO_CIPHER_TYPE_2_2 = UINT8_MAX }; CK_MECHANISM_TYPE cipher_to_nss[] = { 0, /* CRYPTO_CIPHER_TYPE_NONE */ CKM_AES_CBC_PAD, /* CRYPTO_CIPHER_TYPE_AES256 */ CKM_AES_CBC_PAD, /* CRYPTO_CIPHER_TYPE_AES192 */ CKM_AES_CBC_PAD, /* CRYPTO_CIPHER_TYPE_AES128 */ CKM_DES3_CBC_PAD /* CRYPTO_CIPHER_TYPE_3DES */ }; size_t cipher_key_len[] = { 0, /* CRYPTO_CIPHER_TYPE_NONE */ AES_256_KEY_LENGTH, /* CRYPTO_CIPHER_TYPE_AES256 */ AES_192_KEY_LENGTH, /* CRYPTO_CIPHER_TYPE_AES192 */ AES_128_KEY_LENGTH, /* CRYPTO_CIPHER_TYPE_AES128 */ 24 /* CRYPTO_CIPHER_TYPE_3DES - no magic in nss headers */ }; size_t cypher_block_len[] = { 0, /* CRYPTO_CIPHER_TYPE_NONE */ AES_BLOCK_SIZE, /* CRYPTO_CIPHER_TYPE_AES256 */ AES_BLOCK_SIZE, /* CRYPTO_CIPHER_TYPE_AES192 */ AES_BLOCK_SIZE, /* CRYPTO_CIPHER_TYPE_AES128 */ 0 /* CRYPTO_CIPHER_TYPE_3DES */ }; /* * hash definitions and conversion tables */ /* * while CRYPTO_HASH_TYPE_2_X are not a real hash mechanism at all, * we still allocate a value for them because we use crypto_hash_t * internally and we don't want overlaps */ 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, CRYPTO_HASH_TYPE_2_3 = UINT8_MAX - 1, CRYPTO_HASH_TYPE_2_2 = UINT8_MAX }; 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); enum sym_key_type { SYM_KEY_TYPE_CRYPT, SYM_KEY_TYPE_HASH }; #define MAX_WRAPPED_KEY_LEN 128 /* * 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; } else if (strcmp(crypto_cipher_type, "aes192") == 0) { return CRYPTO_CIPHER_TYPE_AES192; } else if (strcmp(crypto_cipher_type, "aes128") == 0) { return CRYPTO_CIPHER_TYPE_AES128; } else if (strcmp(crypto_cipher_type, "3des") == 0) { return CRYPTO_CIPHER_TYPE_3DES; } return CRYPTO_CIPHER_TYPE_AES256; } static PK11SymKey *import_symmetric_key(struct crypto_instance *instance, enum sym_key_type key_type) { SECItem key_item; PK11SlotInfo *slot; PK11SymKey *res_key; CK_MECHANISM_TYPE cipher; CK_ATTRIBUTE_TYPE operation; CK_MECHANISM_TYPE wrap_mechanism; int wrap_key_len; PK11SymKey *wrap_key; PK11Context *wrap_key_crypt_context; SECItem tmp_sec_item; SECItem wrapped_key; int wrapped_key_len; int wrap_key_block_size; unsigned char wrapped_key_data[MAX_WRAPPED_KEY_LEN]; unsigned char pad_key_data[MAX_WRAPPED_KEY_LEN]; int case_processed; memset(&key_item, 0, sizeof(key_item)); slot = NULL; wrap_key = NULL; res_key = NULL; wrap_key_crypt_context = NULL; if (instance->private_key_len > sizeof(pad_key_data)) { log_printf(instance->log_level_security, "Import symmetric key failed. Private key is too long"); goto exit_res_key; } memset(pad_key_data, 0, sizeof(pad_key_data)); memcpy(pad_key_data, instance->private_key, instance->private_key_len); key_item.type = siBuffer; key_item.data = pad_key_data; case_processed = 0; switch (key_type) { case SYM_KEY_TYPE_CRYPT: key_item.len = cipher_key_len[instance->crypto_cipher_type]; cipher = cipher_to_nss[instance->crypto_cipher_type]; operation = CKA_ENCRYPT|CKA_DECRYPT; case_processed = 1; break; case SYM_KEY_TYPE_HASH: key_item.len = instance->private_key_len; cipher = hash_to_nss[instance->crypto_hash_type]; operation = CKA_SIGN; case_processed = 1; break; /* * Default is not defined intentionally. Compiler shows warning when * new key_type is added */ } if (!case_processed) { log_printf(instance->log_level_error, "Unknown key_type"); goto exit_res_key; } slot = PK11_GetBestSlot(cipher, NULL); if (slot == NULL) { log_printf(instance->log_level_security, "Unable to find security slot (%d): %s", PR_GetError(), PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT)); goto exit_res_key; } /* * Without FIPS it would be possible to just use * res_key = PK11_ImportSymKey(slot, cipher, PK11_OriginUnwrap, operation, &key_item, NULL); * with FIPS NSS Level 2 certification has to be "workarounded" (so it becomes Level 1) by using * following method: * 1. Generate wrap key * 2. Encrypt authkey with wrap key * 3. Unwrap encrypted authkey using wrap key */ /* * Generate wrapping key */ wrap_mechanism = PK11_GetBestWrapMechanism(slot); wrap_key_len = PK11_GetBestKeyLength(slot, wrap_mechanism); wrap_key = PK11_KeyGen(slot, wrap_mechanism, NULL, wrap_key_len, NULL); if (wrap_key == NULL) { log_printf(instance->log_level_security, "Unable to generate wrapping key (%d): %s", PR_GetError(), PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT)); goto exit_res_key; } /* * Encrypt authkey with wrapping key */ /* * Key must be padded to a block size */ wrap_key_block_size = PK11_GetBlockSize(wrap_mechanism, 0); if (wrap_key_block_size < 0) { log_printf(instance->log_level_security, "Unable to get wrap key block size (%d): %s", PR_GetError(), PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT)); goto exit_res_key; } if (sizeof(pad_key_data) % wrap_key_block_size != 0) { log_printf(instance->log_level_security, "Padded key buffer size (%zu) is not dividable by " "wrap key block size (%u).", sizeof(pad_key_data), (unsigned int)wrap_key_block_size); goto exit_res_key; } /* * Initialization of IV is not needed because PK11_GetBestWrapMechanism should return ECB mode */ memset(&tmp_sec_item, 0, sizeof(tmp_sec_item)); wrap_key_crypt_context = PK11_CreateContextBySymKey(wrap_mechanism, CKA_ENCRYPT, wrap_key, &tmp_sec_item); if (wrap_key_crypt_context == NULL) { log_printf(instance->log_level_security, "Unable to create encrypt context (%d): %s", PR_GetError(), PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT)); goto exit_res_key; } wrapped_key_len = (int)sizeof(wrapped_key_data); if (PK11_CipherOp(wrap_key_crypt_context, wrapped_key_data, &wrapped_key_len, sizeof(wrapped_key_data), key_item.data, sizeof(pad_key_data)) != SECSuccess) { log_printf(instance->log_level_security, "Unable to encrypt authkey (%d): %s", PR_GetError(), PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT)); goto exit_res_key; } if (PK11_Finalize(wrap_key_crypt_context) != SECSuccess) { log_printf(instance->log_level_security, "Unable to finalize encryption of authkey (%d): %s", PR_GetError(), PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT)); goto exit_res_key; } /* * Finally unwrap sym key */ memset(&tmp_sec_item, 0, sizeof(tmp_sec_item)); wrapped_key.data = wrapped_key_data; wrapped_key.len = wrapped_key_len; res_key = PK11_UnwrapSymKey(wrap_key, wrap_mechanism, &tmp_sec_item, &wrapped_key, cipher, operation, key_item.len); if (res_key == NULL) { log_printf(instance->log_level_security, "Failure to import key into NSS (%d): %s", PR_GetError(), PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT)); goto exit_res_key; } exit_res_key: if (wrap_key_crypt_context != NULL) { PK11_DestroyContext(wrap_key_crypt_context, PR_TRUE); } if (wrap_key != NULL) { PK11_FreeSymKey(wrap_key); } if (slot != NULL) { PK11_FreeSlot(slot); } return (res_key); } static int init_nss_crypto(struct crypto_instance *instance) { if (!cipher_to_nss[instance->crypto_cipher_type]) { return 0; } instance->nss_sym_key = import_symmetric_key(instance, SYM_KEY_TYPE_CRYPT); if (instance->nss_sym_key == NULL) { return -1; } 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 (%d): %s", (int)cipher_to_nss[instance->crypto_cipher_type], PR_GetError(), PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT)); 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) { if (!hash_to_nss[instance->crypto_hash_type]) { return 0; } instance->nss_sym_key_sign = import_symmetric_key(instance, SYM_KEY_TYPE_HASH); if (instance->nss_sym_key_sign == NULL) { return -1; } 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(NULL) != 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_2_3 ( struct crypto_instance *instance, const unsigned char *buf_in, const size_t buf_in_len, unsigned char *buf_out, size_t *buf_out_len) { if (encrypt_nss(instance, buf_in, buf_in_len, buf_out + sizeof(struct crypto_config_header), buf_out_len) < 0) { return -1; } *buf_out_len += sizeof(struct crypto_config_header); if (hash_to_nss[instance->crypto_hash_type]) { if (calculate_nss_hash(instance, buf_out, *buf_out_len, buf_out + *buf_out_len) < 0) { return -1; } *buf_out_len += hash_len[instance->crypto_hash_type]; } return 0; } static int authenticate_nss_2_3 ( 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]]; int datalen = *buf_len - hash_len[instance->crypto_hash_type]; if (*buf_len <= hash_len[instance->crypto_hash_type]) { log_printf(instance->log_level_security, "Received message is too short... ignoring"); return -1; } if (calculate_nss_hash(instance, buf, datalen, tmp_hash) < 0) { return -1; } if (memcmp(tmp_hash, buf + datalen, hash_len[instance->crypto_hash_type]) != 0) { log_printf(instance->log_level_error, "Digest does not match"); return -1; } *buf_len = datalen; } return 0; } static int decrypt_nss_2_3 ( struct crypto_instance *instance, unsigned char *buf, int *buf_len) { *buf_len -= sizeof(struct crypto_config_header); if (decrypt_nss(instance, buf + sizeof(struct crypto_config_header), 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; int block_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; if (cypher_block_len[crypto_cipher]) { block_size = cypher_block_len[crypto_cipher]; } else { block_size = PK11_GetBlockSize(crypto_cipher, NULL); if (block_size < 0) { /* * failsafe. we can potentially lose up to 63 * byte per packet, but better than fragmenting */ block_size = 64; } } hdr_size += (block_size * 2); } return hdr_size; } size_t crypto_get_current_sec_header_size( const struct crypto_instance *instance) { return (instance->crypto_header_size); } /* * 2.0 packet format: * crypto_cipher_type | crypto_hash_type | __pad0 | __pad1 | hash | salt | data * only data is encrypted, hash only covers salt + data * * 2.2/2.3 packet format * fake_crypto_cipher_type | fake_crypto_hash_type | __pad0 | __pad1 | salt | data | hash * only data is encrypted, hash covers the whole packet * * we need to leave fake_* unencrypted for older versions of corosync to reject the packets, * we need to leave __pad0|1 unencrypted for performance reasons (saves at least 2 memcpy and * and extra buffer but values are hashed and verified. */ 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 = CRYPTO_CIPHER_TYPE_2_3; cch->crypto_hash_type = CRYPTO_HASH_TYPE_2_3; cch->__pad0 = 0; cch->__pad1 = 0; err = encrypt_and_sign_nss_2_3(instance, buf_in, buf_in_len, buf_out, buf_out_len); 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; const char *guessed_str; if (*buf_len <= sizeof(struct crypto_config_header)) { log_printf(instance->log_level_security, "Received message is too short... ignoring"); return (-1); } if (cch->crypto_cipher_type != CRYPTO_CIPHER_TYPE_2_3) { guessed_str = NULL; if ((cch->crypto_cipher_type == 0xC0 && cch->crypto_hash_type == 0x70) || (cch->crypto_cipher_type == 0x70 && cch->crypto_hash_type == 0xC0)) { guessed_str = "Corosync 3.x"; } else if (cch->crypto_cipher_type == CRYPTO_CIPHER_TYPE_2_2) { guessed_str = "Corosync 2.2"; } else if (cch->crypto_cipher_type == 0x01) { guessed_str = "unencrypted Kronosnet"; } else if (cch->crypto_cipher_type >= 0 && cch->crypto_cipher_type <= 5) { guessed_str = "unencrypted Corosync 2.0/2.1/1.x/OpenAIS"; } else { guessed_str = "encrypted Kronosnet/Corosync 2.0/2.1/1.x/OpenAIS or unknown"; } log_printf(instance->log_level_security, "Unsupported incoming packet (probably sent by %s). Rejecting", guessed_str); return -1; } if (cch->crypto_hash_type != CRYPTO_HASH_TYPE_2_3) { log_printf(instance->log_level_security, "Incoming packet has different hash type. Rejecting"); return -1; } /* * authenticate packet first */ if (authenticate_nss_2_3(instance, buf, buf_len) != 0) { return -1; } /* * now we can "trust" the padding bytes/future features */ 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; } /* * decrypt */ if (decrypt_nss_2_3(instance, buf, buf_len) != 0) { return -1; } /* * invalidate config header and kill it */ cch = NULL; memmove(buf, buf + sizeof(struct crypto_config_header), *buf_len); return 0; } 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); }