/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #define _DES_FIPS_POST #ifndef _KERNEL #include #include #include #include #include #include #include "softCrypt.h" #else #define _DES_IMPL #include #endif #ifndef _KERNEL /* * Allocate context for the DES encryption or decryption operation, and * generate DES or DES3 key schedule to speed up the operation. */ soft_des_ctx_t * des_build_context(uint8_t *key, uint8_t *iv, CK_KEY_TYPE key_type, CK_MECHANISM_TYPE mechanism) { size_t size; soft_des_ctx_t *soft_des_ctx; soft_des_ctx = calloc(1, sizeof (soft_des_ctx_t)); if (soft_des_ctx == NULL) { return (NULL); } /* Allocate key schedule for DES or DES3 based on key type. */ if (key_type == CKK_DES) { soft_des_ctx->key_sched = des_alloc_keysched(&size, DES, 0); if (soft_des_ctx->key_sched == NULL) { free(soft_des_ctx); return (NULL); } des_init_keysched(key, DES, soft_des_ctx->key_sched); } else { soft_des_ctx->key_sched = des_alloc_keysched(&size, DES3, 0); if (soft_des_ctx->key_sched == NULL) { free(soft_des_ctx); return (NULL); } des_init_keysched(key, DES3, soft_des_ctx->key_sched); } soft_des_ctx->keysched_len = size; soft_des_ctx->key_type = key_type; if ((mechanism == CKM_DES_CBC) || (mechanism == CKM_DES3_CBC)) { /* Save Initialization Vector (IV) in the context. */ (void) memcpy(soft_des_ctx->ivec, iv, DES_BLOCK_LEN); /* Allocate a context for DES cipher-block chaining. */ soft_des_ctx->des_cbc = (void *)des_cbc_ctx_init( soft_des_ctx->key_sched, soft_des_ctx->keysched_len, soft_des_ctx->ivec, soft_des_ctx->key_type); if (soft_des_ctx->des_cbc == NULL) { bzero(soft_des_ctx->key_sched, soft_des_ctx->keysched_len); free(soft_des_ctx->key_sched); return (NULL); } } return (soft_des_ctx); } /* * Free the DES context. */ void fips_des_free_context(soft_des_ctx_t *soft_des_ctx) { des_ctx_t *des_ctx; des_ctx = (des_ctx_t *)soft_des_ctx->des_cbc; if (des_ctx != NULL) { bzero(des_ctx->dc_keysched, des_ctx->dc_keysched_len); free(soft_des_ctx->des_cbc); } bzero(soft_des_ctx->key_sched, soft_des_ctx->keysched_len); free(soft_des_ctx->key_sched); free(soft_des_ctx); } #else static void des_copy_block64(uint8_t *in, uint64_t *out) { if (IS_P2ALIGNED(in, sizeof (uint64_t))) { /* LINTED: pointer alignment */ out[0] = *(uint64_t *)&in[0]; } else { uint64_t tmp64; #ifdef _BIG_ENDIAN tmp64 = (((uint64_t)in[0] << 56) | ((uint64_t)in[1] << 48) | ((uint64_t)in[2] << 40) | ((uint64_t)in[3] << 32) | ((uint64_t)in[4] << 24) | ((uint64_t)in[5] << 16) | ((uint64_t)in[6] << 8) | (uint64_t)in[7]); #else tmp64 = (((uint64_t)in[7] << 56) | ((uint64_t)in[6] << 48) | ((uint64_t)in[5] << 40) | ((uint64_t)in[4] << 32) | ((uint64_t)in[3] << 24) | ((uint64_t)in[2] << 16) | ((uint64_t)in[1] << 8) | (uint64_t)in[0]); #endif /* _BIG_ENDIAN */ out[0] = tmp64; } } des_ctx_t * des_build_context(uint8_t *key, uint8_t *iv, des_mech_type_t mech_type) { int rv = CRYPTO_SUCCESS; void *keysched; size_t size; des_ctx_t *des_ctx = NULL; des_strength_t strength; switch (mech_type) { case DES_ECB_MECH_INFO_TYPE: des_ctx = ecb_alloc_ctx(KM_SLEEP); /* FALLTHRU */ case DES_CBC_MECH_INFO_TYPE: strength = DES; if (des_ctx == NULL) des_ctx = cbc_alloc_ctx(KM_SLEEP); break; case DES3_ECB_MECH_INFO_TYPE: des_ctx = ecb_alloc_ctx(KM_SLEEP); /* FALLTHRU */ case DES3_CBC_MECH_INFO_TYPE: strength = DES3; if (des_ctx == NULL) des_ctx = cbc_alloc_ctx(KM_SLEEP); break; default: return (NULL); } if ((keysched = des_alloc_keysched(&size, strength, KM_SLEEP)) == NULL) return (NULL); /* * Initialize key schedule. * Key length is stored in the key. */ des_init_keysched(key, strength, keysched); des_ctx->dc_flags |= PROVIDER_OWNS_KEY_SCHEDULE; des_ctx->dc_keysched_len = size; des_ctx->dc_keysched = keysched; if (strength == DES3) { des_ctx->dc_flags |= DES3_STRENGTH; } switch (mech_type) { case DES_CBC_MECH_INFO_TYPE: case DES3_CBC_MECH_INFO_TYPE: /* Save Initialization Vector (IV) in the context. */ rv = cbc_init_ctx((cbc_ctx_t *)des_ctx, (char *)iv, DES_BLOCK_LEN, DES_BLOCK_LEN, des_copy_block64); break; case DES_ECB_MECH_INFO_TYPE: case DES3_ECB_MECH_INFO_TYPE: des_ctx->dc_flags |= ECB_MODE; } if (rv != CRYPTO_SUCCESS) { if (des_ctx->dc_flags & PROVIDER_OWNS_KEY_SCHEDULE) { bzero(keysched, size); kmem_free(keysched, size); } } return (des_ctx); } void fips_des_free_context(des_ctx_t *des_ctx) { if (des_ctx != NULL) { if (des_ctx->dc_flags & PROVIDER_OWNS_KEY_SCHEDULE) { ASSERT(des_ctx->dc_keysched_len != 0); bzero(des_ctx->dc_keysched, des_ctx->dc_keysched_len); kmem_free(des_ctx->dc_keysched, des_ctx->dc_keysched_len); } bzero(des_ctx, sizeof (des_ctx_t)); kmem_free(des_ctx, sizeof (des_ctx_t)); } } #endif /* * fips_des_encrypt() * * Arguments: * soft_des_ctx: pointer to DES context * in_buf: pointer to the input data to be encrypted * ulDataLen: length of the input data * out_buf: pointer to the output data after encryption * pulEncryptedLen: pointer to the length of the output data * mechanism: CKM_DES_ECB, CKM_DES3_ECB, CKM_DES_CBC, CKM_DES3_CBC * * Description: * This function calls the corresponding DES low-level encrypt * routine based on the mechanism. * */ #ifndef _KERNEL CK_RV fips_des_encrypt(soft_des_ctx_t *soft_des_ctx, CK_BYTE_PTR in_buf, CK_ULONG ulDataLen, CK_BYTE_PTR out_buf, CK_ULONG_PTR pulEncryptedLen, CK_MECHANISM_TYPE mechanism) #else int fips_des_encrypt(des_ctx_t *des_ctx, uint8_t *in_buf, ulong_t ulDataLen, uint8_t *out_buf, ulong_t *pulEncryptedLen, des_mech_type_t mechanism) #endif { CK_RV rv = CKR_OK; int rc = 0; ulong_t out_len; /* * DES only takes input length that is a multiple of blocksize * with the mechanism CKM_DES_ECB or CKM_DES_CBC. */ if ((ulDataLen % DES_BLOCK_LEN) != 0) { return (CKR_DATA_LEN_RANGE); } /* * For non-padding mode, the output length will * be same as the input length. */ out_len = ulDataLen; /* * Begin Encryption now. */ switch (mechanism) { case CKM_DES_ECB: case CKM_DES3_ECB: { ulong_t i; uint8_t *tmp_inbuf; uint8_t *tmp_outbuf; for (i = 0; i < out_len; i += DES_BLOCK_LEN) { tmp_inbuf = &in_buf[i]; tmp_outbuf = &out_buf[i]; /* Crunch one block of data for DES. */ #ifndef _KERNEL if (soft_des_ctx->key_type == CKK_DES) (void) des_crunch_block( soft_des_ctx->key_sched, tmp_inbuf, tmp_outbuf, B_FALSE); else (void) des3_crunch_block( soft_des_ctx->key_sched, tmp_inbuf, tmp_outbuf, B_FALSE); #else if (mechanism == DES_ECB_MECH_INFO_TYPE) (void) des_crunch_block(des_ctx->dc_keysched, tmp_inbuf, tmp_outbuf, B_FALSE); else (void) des3_crunch_block(des_ctx->dc_keysched, tmp_inbuf, tmp_outbuf, B_FALSE); #endif } *pulEncryptedLen = out_len; break; } case CKM_DES_CBC: case CKM_DES3_CBC: { crypto_data_t out; out.cd_format = CRYPTO_DATA_RAW; out.cd_offset = 0; out.cd_length = out_len; out.cd_raw.iov_base = (char *)out_buf; out.cd_raw.iov_len = out_len; /* Encrypt multiple blocks of data. */ rc = des_encrypt_contiguous_blocks( #ifndef _KERNEL (des_ctx_t *)soft_des_ctx->des_cbc, #else des_ctx, #endif (char *)in_buf, out_len, &out); if (rc != 0) goto encrypt_failed; if (rc == 0) { *pulEncryptedLen = out_len; break; } encrypt_failed: *pulEncryptedLen = 0; return (CKR_DEVICE_ERROR); } } /* end switch */ return (rv); } /* * fips_des_decrypt() * * Arguments: * soft_des_ctx: pointer to DES context * in_buf: pointer to the input data to be decrypted * ulEncryptedLen: length of the input data * out_buf: pointer to the output data * pulDataLen: pointer to the length of the output data * mechanism: CKM_DES_ECB, CKM_DES3_ECB, CKM_DES_CBC, CKM_DES3_CBC * * Description: * This function calls the corresponding DES low-level decrypt * function based on the mechanism. * */ #ifndef _KERNEL CK_RV fips_des_decrypt(soft_des_ctx_t *soft_des_ctx, CK_BYTE_PTR in_buf, CK_ULONG ulEncryptedLen, CK_BYTE_PTR out_buf, CK_ULONG_PTR pulDataLen, CK_MECHANISM_TYPE mechanism) #else int fips_des_decrypt(des_ctx_t *des_ctx, uint8_t *in_buf, ulong_t ulEncryptedLen, uint8_t *out_buf, ulong_t *pulDataLen, des_mech_type_t mechanism) #endif { CK_RV rv = CKR_OK; int rc = 0; ulong_t out_len; /* * DES only takes input length that is a multiple of 8 bytes * with the mechanism CKM_DES_ECB, CKM_DES_CBC or * CKM_DES_CBC_PAD. */ if ((ulEncryptedLen % DES_BLOCK_LEN) != 0) { return (CKR_DATA_LEN_RANGE); } /* Set output length same as input length. */ out_len = ulEncryptedLen; /* * Begin Decryption. */ switch (mechanism) { case CKM_DES_ECB: case CKM_DES3_ECB: { uint8_t *tmp_inbuf; uint8_t *tmp_outbuf; ulong_t i; for (i = 0; i < out_len; i += DES_BLOCK_LEN) { tmp_inbuf = &in_buf[i]; tmp_outbuf = &out_buf[i]; /* Crunch one block of data for DES. */ #ifndef _KERNEL if (soft_des_ctx->key_type == CKK_DES) (void) des_crunch_block( soft_des_ctx->key_sched, tmp_inbuf, tmp_outbuf, B_TRUE); else (void) des3_crunch_block( soft_des_ctx->key_sched, tmp_inbuf, tmp_outbuf, B_TRUE); #else if (mechanism == DES_ECB_MECH_INFO_TYPE) (void) des_crunch_block(des_ctx->dc_keysched, tmp_inbuf, tmp_outbuf, B_TRUE); else (void) des3_crunch_block(des_ctx->dc_keysched, tmp_inbuf, tmp_outbuf, B_TRUE); #endif } *pulDataLen = out_len; break; } case CKM_DES_CBC: case CKM_DES3_CBC: { crypto_data_t out; out.cd_format = CRYPTO_DATA_RAW; out.cd_offset = 0; out.cd_length = out_len; out.cd_raw.iov_base = (char *)out_buf; out.cd_raw.iov_len = out_len; /* Decrypt multiple blocks of data. */ rc = des_decrypt_contiguous_blocks( #ifndef _KERNEL (des_ctx_t *)soft_des_ctx->des_cbc, #else des_ctx, #endif (char *)in_buf, out_len, &out); if (rc != 0) goto decrypt_failed; *pulDataLen = out_len; if (rc == 0) break; decrypt_failed: *pulDataLen = 0; return (CKR_DEVICE_ERROR); } } /* end switch */ return (rv); } /* * DES3 Power-On SelfTest(s). */ int fips_des3_post(void) { /* DES3 Known Key. */ static uint8_t des3_known_key[] = { "ANSI Triple-DES Key Data" }; /* DES3-CBC Known Initialization Vector (64-bits). */ static uint8_t des3_cbc_known_iv[] = { "Security" }; /* DES3 Known Plaintext (64-bits). */ static uint8_t des3_ecb_known_plaintext[] = { "Solaris!" }; static uint8_t des3_cbc_known_plaintext[] = { "Solaris!" }; /* DES3 Known Ciphertext (64-bits). */ static uint8_t des3_ecb_known_ciphertext[] = { 0x17, 0x0d, 0x1f, 0x13, 0xd3, 0xa0, 0x3a, 0x63 }; static uint8_t des3_cbc_known_ciphertext[] = { 0x7f, 0x62, 0x44, 0xb3, 0xf8, 0x77, 0xf8, 0xf8 }; /* DES3 variables. */ uint8_t des3_computed_ciphertext[FIPS_DES3_ENCRYPT_LENGTH]; uint8_t des3_computed_plaintext[FIPS_DES3_DECRYPT_LENGTH]; #ifdef _KERNEL des_ctx_t *des3_context; #else soft_des_ctx_t *des3_context; #endif ulong_t des3_bytes_encrypted; ulong_t des3_bytes_decrypted; int rv; /* * DES3 ECB Known Answer Encryption Test */ #ifdef _KERNEL des3_context = des_build_context(des3_known_key, NULL, DES3_ECB_MECH_INFO_TYPE); #else des3_context = des_build_context(des3_known_key, NULL, CKK_DES3, CKM_DES3_ECB); #endif if (des3_context == NULL) return (CKR_HOST_MEMORY); #ifdef _KERNEL rv = fips_des_encrypt(des3_context, des3_ecb_known_plaintext, FIPS_DES3_ENCRYPT_LENGTH, des3_computed_ciphertext, &des3_bytes_encrypted, DES3_ECB_MECH_INFO_TYPE); #else rv = fips_des_encrypt(des3_context, des3_ecb_known_plaintext, FIPS_DES3_ENCRYPT_LENGTH, des3_computed_ciphertext, &des3_bytes_encrypted, CKM_DES3_ECB); #endif fips_des_free_context(des3_context); if ((rv != CRYPTO_SUCCESS) || (des3_bytes_encrypted != FIPS_DES3_ENCRYPT_LENGTH) || (memcmp(des3_computed_ciphertext, des3_ecb_known_ciphertext, FIPS_DES3_ENCRYPT_LENGTH) != 0)) return (CKR_DEVICE_ERROR); /* * DES3 ECB Known Answer Decryption Test */ #ifdef _KERNEL des3_context = des_build_context(des3_known_key, NULL, DES3_ECB_MECH_INFO_TYPE); #else des3_context = des_build_context(des3_known_key, NULL, CKK_DES3, CKM_DES3_ECB); #endif if (des3_context == NULL) return (CKR_HOST_MEMORY); #ifdef _KERNEL rv = fips_des_decrypt(des3_context, des3_ecb_known_ciphertext, FIPS_DES3_DECRYPT_LENGTH, des3_computed_plaintext, &des3_bytes_decrypted, DES3_ECB_MECH_INFO_TYPE); #else rv = fips_des_decrypt(des3_context, des3_ecb_known_ciphertext, FIPS_DES3_DECRYPT_LENGTH, des3_computed_plaintext, &des3_bytes_decrypted, CKM_DES3_ECB); #endif fips_des_free_context(des3_context); if ((rv != CRYPTO_SUCCESS) || (des3_bytes_decrypted != FIPS_DES3_DECRYPT_LENGTH) || (memcmp(des3_computed_plaintext, des3_ecb_known_plaintext, FIPS_DES3_DECRYPT_LENGTH) != 0)) return (CKR_DEVICE_ERROR); /* * DES3 CBC Known Answer Encryption Test */ #ifdef _KERNEL des3_context = des_build_context(des3_known_key, des3_cbc_known_iv, DES3_CBC_MECH_INFO_TYPE); #else des3_context = des_build_context(des3_known_key, des3_cbc_known_iv, CKK_DES3, CKM_DES3_CBC); #endif if (des3_context == NULL) return (CKR_HOST_MEMORY); #ifdef _KERNEL rv = fips_des_encrypt(des3_context, des3_cbc_known_plaintext, FIPS_DES3_ENCRYPT_LENGTH, des3_computed_ciphertext, &des3_bytes_encrypted, DES3_CBC_MECH_INFO_TYPE); #else rv = fips_des_encrypt(des3_context, des3_cbc_known_plaintext, FIPS_DES3_ENCRYPT_LENGTH, des3_computed_ciphertext, &des3_bytes_encrypted, CKM_DES3_CBC); #endif fips_des_free_context(des3_context); if ((rv != CRYPTO_SUCCESS) || (des3_bytes_encrypted != FIPS_DES3_ENCRYPT_LENGTH) || (memcmp(des3_computed_ciphertext, des3_cbc_known_ciphertext, FIPS_DES3_ENCRYPT_LENGTH) != 0)) return (CKR_DEVICE_ERROR); /* * DES3 CBC Known Answer Decryption Test */ #ifdef _KERNEL des3_context = des_build_context(des3_known_key, des3_cbc_known_iv, DES3_CBC_MECH_INFO_TYPE); #else des3_context = des_build_context(des3_known_key, des3_cbc_known_iv, CKK_DES3, CKM_DES3_CBC); #endif if (des3_context == NULL) return (CKR_HOST_MEMORY); #ifdef _KERNEL rv = fips_des_decrypt(des3_context, des3_cbc_known_ciphertext, FIPS_DES3_DECRYPT_LENGTH, des3_computed_plaintext, &des3_bytes_decrypted, DES3_CBC_MECH_INFO_TYPE); #else rv = fips_des_decrypt(des3_context, des3_cbc_known_ciphertext, FIPS_DES3_DECRYPT_LENGTH, des3_computed_plaintext, &des3_bytes_decrypted, CKM_DES3_CBC); #endif fips_des_free_context(des3_context); if ((rv != CRYPTO_SUCCESS) || (des3_bytes_decrypted != FIPS_DES3_DECRYPT_LENGTH) || (memcmp(des3_computed_plaintext, des3_cbc_known_plaintext, FIPS_DES3_DECRYPT_LENGTH) != 0)) return (CKR_DEVICE_ERROR); return (CKR_OK); }