/* * 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) * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LOGSYS_UTILS_ONLY 1 #include #include #include "totemcrypto.h" #include "util.h" #include #include #include #include #define CRYPTO_HMAC_HASH_SIZE 20 struct crypto_security_header { unsigned char hash_digest[CRYPTO_HMAC_HASH_SIZE]; /* The hash *MUST* be first in the data structure */ unsigned char salt[16]; /* random number */ char msg[0]; } __attribute__((packed)); 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; 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; }; 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 */ }; CK_MECHANISM_TYPE hash_to_nss[] = { 0, /* CRYPTO_HASH_TYPE_NONE */ CKM_SHA_1_HMAC /* CRYPTO_HASH_TYPE_SHA1 */ }; #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); #define LOGSYS_PERROR(err_num, level, fmt, args...) \ do { \ char _error_str[LOGSYS_MAX_PERROR_MSG_LEN]; \ const char *_error_ptr = qb_strerror_r(err_num, _error_str, sizeof(_error_str)); \ instance->totemudp_log_printf ( \ level, instance->log_subsys_id, \ __FUNCTION__, __FILE__, __LINE__, \ fmt ": %s (%d)", ##args, _error_ptr, err_num); \ } while(0) static void init_nss_crypto(struct crypto_instance *instance) { PK11SlotInfo* aes_slot = NULL; PK11SlotInfo* sha1_slot = NULL; SECItem key_item; SECStatus rv; log_printf(instance->log_level_notice, "Initializing transmit/receive security: NSS AES256CBC/SHA1HMAC (mode %u).", 0); rv = NSS_NoDB_Init("."); if (rv != SECSuccess) { log_printf(instance->log_level_security, "NSS initialization failed (err %d)", PR_GetError()); goto out; } /* * TODO: use instance info! */ aes_slot = PK11_GetBestSlot(cipher_to_nss[instance->crypto_cipher_type], NULL); if (aes_slot == NULL) { log_printf(instance->log_level_security, "Unable to find security slot (err %d)", PR_GetError()); goto out; } sha1_slot = PK11_GetBestSlot(hash_to_nss[instance->crypto_hash_type], NULL); if (sha1_slot == NULL) { log_printf(instance->log_level_security, "Unable to find security slot (err %d)", PR_GetError()); goto out; } /* * Make the private key into a SymKey that we can use */ key_item.type = siBuffer; key_item.data = instance->private_key; key_item.len = cipher_key_len[instance->crypto_cipher_type]; instance->nss_sym_key = PK11_ImportSymKey(aes_slot, cipher_to_nss[instance->crypto_cipher_type], PK11_OriginUnwrap, CKA_ENCRYPT|CKA_DECRYPT, &key_item, NULL); if (instance->nss_sym_key == NULL) { log_printf(instance->log_level_security, "Failure to import key into NSS (err %d)", PR_GetError()); goto out; } instance->nss_sym_key_sign = PK11_ImportSymKey(sha1_slot, hash_to_nss[instance->crypto_hash_type], PK11_OriginUnwrap, CKA_SIGN, &key_item, NULL); if (instance->nss_sym_key_sign == NULL) { log_printf(instance->log_level_security, "Failure to import key into NSS (err %d)", PR_GetError()); goto out; } out: return; } 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) { PK11Context* enc_context = NULL; SECStatus rv1, rv2; int tmp1_outlen; unsigned int tmp2_outlen; unsigned char *outdata; SECItem no_params; SECItem iv_item; struct crypto_security_header *header; SECItem *nss_sec_param; unsigned char nss_iv_data[16]; SECStatus rv; no_params.type = siBuffer; no_params.data = 0; no_params.len = 0; tmp1_outlen = tmp2_outlen = 0; outdata = buf_out + sizeof (struct crypto_security_header); header = (struct crypto_security_header *)buf_out; rv = PK11_GenerateRandom ( nss_iv_data, sizeof (nss_iv_data)); if (rv != SECSuccess) { log_printf(instance->log_level_security, "Failure to generate a random number %d", PR_GetError()); } memcpy(header->salt, nss_iv_data, sizeof(nss_iv_data)); iv_item.type = siBuffer; iv_item.data = nss_iv_data; iv_item.len = sizeof (nss_iv_data); nss_sec_param = PK11_ParamFromIV ( cipher_to_nss[instance->crypto_cipher_type], &iv_item); if (nss_sec_param == NULL) { log_printf(instance->log_level_security, "Failure to set up PKCS11 param (err %d)", PR_GetError()); return (-1); } /* * Create cipher context for encryption */ enc_context = PK11_CreateContextBySymKey ( cipher_to_nss[instance->crypto_cipher_type], CKA_ENCRYPT, instance->nss_sym_key, nss_sec_param); if (!enc_context) { char err[1024]; PR_GetErrorText(err); err[PR_GetErrorTextLength()] = 0; log_printf(instance->log_level_security, "PK11_CreateContext failed (encrypt) crypt_type=%d (err %d): %s", (int)cipher_to_nss[instance->crypto_cipher_type], PR_GetError(), err); return -1; } rv1 = PK11_CipherOp(enc_context, outdata, &tmp1_outlen, FRAME_SIZE_MAX - sizeof(struct crypto_security_header), (unsigned char *)buf_in, buf_in_len); rv2 = PK11_DigestFinal(enc_context, outdata + tmp1_outlen, &tmp2_outlen, FRAME_SIZE_MAX - tmp1_outlen); PK11_DestroyContext(enc_context, PR_TRUE); *buf_out_len = tmp1_outlen + tmp2_outlen; if (rv1 != SECSuccess || rv2 != SECSuccess) goto out; /* Now do the digest */ enc_context = PK11_CreateContextBySymKey(hash_to_nss[instance->crypto_hash_type], CKA_SIGN, instance->nss_sym_key_sign, &no_params); if (!enc_context) { char err[1024]; PR_GetErrorText(err); err[PR_GetErrorTextLength()] = 0; log_printf(instance->log_level_security, "encrypt: PK11_CreateContext failed (digest) err %d: %s", PR_GetError(), err); return -1; } PK11_DigestBegin(enc_context); rv1 = PK11_DigestOp(enc_context, outdata - 16, *buf_out_len + 16); rv2 = PK11_DigestFinal(enc_context, header->hash_digest, &tmp2_outlen, sizeof(header->hash_digest)); PK11_DestroyContext(enc_context, PR_TRUE); if (rv1 != SECSuccess || rv2 != SECSuccess) goto out; *buf_out_len = *buf_out_len + sizeof(struct crypto_security_header); SECITEM_FreeItem(nss_sec_param, PR_TRUE); return 0; out: return -1; } static int authenticate_and_decrypt_nss ( struct crypto_instance *instance, unsigned char *buf, int *buf_len) { PK11Context* enc_context = NULL; SECStatus rv1, rv2; int tmp1_outlen; unsigned int tmp2_outlen; unsigned char outbuf[FRAME_SIZE_MAX]; unsigned char digest[CRYPTO_HMAC_HASH_SIZE]; unsigned char *outdata; int result_len; unsigned char *data; unsigned char *inbuf; size_t datalen; struct crypto_security_header *header = (struct crypto_security_header *)buf; SECItem no_params; SECItem ivdata; no_params.type = siBuffer; no_params.data = 0; no_params.len = 0; tmp1_outlen = tmp2_outlen = 0; inbuf = (unsigned char *)buf; datalen = *buf_len; data = inbuf + sizeof (struct crypto_security_header) - 16; datalen = datalen - sizeof (struct crypto_security_header) + 16; outdata = outbuf + sizeof (struct crypto_security_header); /* Check the digest */ enc_context = PK11_CreateContextBySymKey ( hash_to_nss[instance->crypto_hash_type], CKA_SIGN, instance->nss_sym_key_sign, &no_params); if (!enc_context) { char err[1024]; PR_GetErrorText(err); err[PR_GetErrorTextLength()] = 0; log_printf(instance->log_level_security, "PK11_CreateContext failed (check digest) err %d: %s", PR_GetError(), err); return -1; } PK11_DigestBegin(enc_context); rv1 = PK11_DigestOp(enc_context, data, datalen); rv2 = PK11_DigestFinal(enc_context, digest, &tmp2_outlen, sizeof(digest)); PK11_DestroyContext(enc_context, PR_TRUE); if (rv1 != SECSuccess || rv2 != SECSuccess) { log_printf(instance->log_level_security, "Digest check failed"); return -1; } if (memcmp(digest, header->hash_digest, tmp2_outlen) != 0) { log_printf(instance->log_level_error, "Digest does not match"); return -1; } /* * Get rid of salt */ data += 16; datalen -= 16; /* Create cipher context for decryption */ ivdata.type = siBuffer; ivdata.data = header->salt; ivdata.len = sizeof(header->salt); enc_context = PK11_CreateContextBySymKey( cipher_to_nss[instance->crypto_cipher_type], CKA_DECRYPT, instance->nss_sym_key, &ivdata); if (!enc_context) { log_printf(instance->log_level_security, "PK11_CreateContext (decrypt) failed (err %d)", PR_GetError()); return -1; } rv1 = PK11_CipherOp(enc_context, outdata, &tmp1_outlen, sizeof(outbuf) - sizeof (struct crypto_security_header), data, datalen); if (rv1 != SECSuccess) { log_printf(instance->log_level_security, "PK11_CipherOp (decrypt) failed (err %d)", PR_GetError()); } rv2 = PK11_DigestFinal(enc_context, outdata + tmp1_outlen, &tmp2_outlen, sizeof(outbuf) - tmp1_outlen); PK11_DestroyContext(enc_context, PR_TRUE); result_len = tmp1_outlen + tmp2_outlen + sizeof (struct crypto_security_header); memset(buf, 0, *buf_len); memcpy(buf, outdata, result_len); *buf_len = result_len; if (rv1 != SECSuccess || rv2 != SECSuccess) return -1; return 0; } size_t crypto_sec_header_size(const char *crypto_hash_type) { /* * TODO: add switch / size mapping */ return sizeof(struct crypto_security_header); } 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) { return (encrypt_and_sign_nss(instance, buf_in, buf_in_len, buf_out, buf_out_len)); } int crypto_authenticate_and_decrypt (struct crypto_instance *instance, unsigned char *buf, int *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; if (strcmp(crypto_cipher_type, "none") == 0) { instance->crypto_cipher_type = CRYPTO_CIPHER_TYPE_NONE; } else if (strcmp(crypto_cipher_type, "aes256") == 0) { instance->crypto_cipher_type = CRYPTO_CIPHER_TYPE_AES256; } if (strcmp(crypto_hash_type, "none") == 0) { instance->crypto_hash_type = CRYPTO_HASH_TYPE_NONE; } else if (strcmp(crypto_hash_type, "sha1") == 0) { instance->crypto_hash_type = CRYPTO_HASH_TYPE_SHA1; } 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; init_nss_crypto(instance); return (instance); }