root/usr/src/lib/pkcs11/pkcs11_tpm/common/key_mgr.c
/*
 * The Initial Developer of the Original Code is International
 * Business Machines Corporation. Portions created by IBM
 * Corporation are Copyright (C) 2005 International Business
 * Machines Corporation. All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the Common Public License as published by
 * IBM Corporation; either version 1 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * Common Public License for more details.
 *
 * You should have received a copy of the Common Public License
 * along with this program; if not, a copy can be viewed at
 * http://www.opensource.org/licenses/cpl1.0.php.
 */
/* (C) COPYRIGHT International Business Machines Corp. 2001, 2002, 2005 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include "tpmtok_int.h"

static CK_BBOOL true = TRUE, false = FALSE;

static CK_RV
key_mgr_get_private_key_type(
        CK_BYTE *keydata,
        CK_ULONG        keylen,
        CK_KEY_TYPE *keytype)
{
        CK_BYTE  *alg = NULL;
        CK_BYTE  *priv_key = NULL;
        CK_ULONG  alg_len;
        CK_RV    rc;

        rc = ber_decode_PrivateKeyInfo(keydata, keylen, &alg,
            &alg_len, &priv_key);
        if (rc != CKR_OK) {
                return (rc);
        }
        if (alg_len >= ber_rsaEncryptionLen) {
                if (memcmp(alg, ber_rsaEncryption,
                    ber_rsaEncryptionLen) == 0) {
                        *keytype = CKK_RSA;
                        return (CKR_OK);
                }
        }

        return (CKR_TEMPLATE_INCOMPLETE);
}

CK_RV
key_mgr_generate_key_pair(SESSION          * sess,
        CK_MECHANISM    * mech,
        CK_ATTRIBUTE    * publ_tmpl,
        CK_ULONG            publ_count,
        CK_ATTRIBUTE    * priv_tmpl,
        CK_ULONG            priv_count,
        CK_OBJECT_HANDLE  * publ_key_handle,
        CK_OBJECT_HANDLE  * priv_key_handle)
{
        OBJECT  * publ_key_obj = NULL;
        OBJECT  * priv_key_obj = NULL;
        CK_ATTRIBUTE  * attr     = NULL;
        CK_ATTRIBUTE  * new_attr        = NULL;
        CK_ULONG        i, keyclass, subclass = 0;
        CK_BBOOL        flag;
        CK_RV      rc;

        if (! sess || ! mech || ! publ_key_handle || ! priv_key_handle) {
                return (CKR_FUNCTION_FAILED);
        }
        if (! publ_tmpl && (publ_count != 0)) {
                return (CKR_FUNCTION_FAILED);
        }
        if (! priv_tmpl && (priv_count != 0)) {
                return (CKR_FUNCTION_FAILED);
        }

        for (i = 0; i < publ_count; i++) {
                if (publ_tmpl[i].type == CKA_CLASS) {
                        keyclass = *(CK_OBJECT_CLASS *)publ_tmpl[i].pValue;
                        if (keyclass != CKO_PUBLIC_KEY) {
                                return (CKR_TEMPLATE_INCONSISTENT);
                        }
                }

                if (publ_tmpl[i].type == CKA_KEY_TYPE)
                        subclass = *(CK_ULONG *)publ_tmpl[i].pValue;
        }


        for (i = 0; i < priv_count; i++) {
                if (priv_tmpl[i].type == CKA_CLASS) {
                        keyclass = *(CK_OBJECT_CLASS *)priv_tmpl[i].pValue;
                        if (keyclass != CKO_PRIVATE_KEY) {
                                return (CKR_TEMPLATE_INCONSISTENT);
                        }
                }

                if (priv_tmpl[i].type == CKA_KEY_TYPE) {
                        CK_ULONG temp = *(CK_ULONG *)priv_tmpl[i].pValue;
                        if (temp != subclass) {
                                return (CKR_TEMPLATE_INCONSISTENT);
                        }
                }
        }


        switch (mech->mechanism) {
                case CKM_RSA_PKCS_KEY_PAIR_GEN:
                        if (subclass != 0 && subclass != CKK_RSA) {
                                return (CKR_TEMPLATE_INCONSISTENT);
                        }

                        subclass = CKK_RSA;
                break;

                default:
                        return (CKR_MECHANISM_INVALID);
        }


        rc = object_mgr_create_skel(sess,
            publ_tmpl,  publ_count, MODE_KEYGEN,
            CKO_PUBLIC_KEY,  subclass, &publ_key_obj);

        if (rc != CKR_OK) {
                goto error;
        }
        rc = object_mgr_create_skel(sess,
            priv_tmpl,  priv_count, MODE_KEYGEN,
            CKO_PRIVATE_KEY, subclass, &priv_key_obj);

        if (rc != CKR_OK) {
                goto error;
        }

        switch (mech->mechanism) {
                case CKM_RSA_PKCS_KEY_PAIR_GEN:
                        rc = ckm_rsa_key_pair_gen(
                            sess->hContext,
                            publ_key_obj->template,
                            priv_key_obj->template);
                break;

                default:
                        rc = CKR_MECHANISM_INVALID;
                break;
        }

        if (rc != CKR_OK) {
                goto error;
        }

        /*
         * we can now set CKA_ALWAYS_SENSITIVE and CKA_NEVER_EXTRACTABLE
         * to their appropriate values.  this only applies to CKO_SECRET_KEY
         * and CKO_PRIVATE_KEY objects
         */
        flag = template_attribute_find(priv_key_obj->template,
            CKA_SENSITIVE, &attr);
        if (flag == TRUE) {
                flag = *(CK_BBOOL *)attr->pValue;

                rc = build_attribute(CKA_ALWAYS_SENSITIVE, &flag,
                    sizeof (CK_BBOOL), &new_attr);
                if (rc != CKR_OK) {
                        goto error;
                }
                (void) template_update_attribute(priv_key_obj->template,
                    new_attr);

        } else {
                rc = CKR_FUNCTION_FAILED;
                goto error;
        }


        flag = template_attribute_find(priv_key_obj->template,
            CKA_EXTRACTABLE, &attr);
        if (flag == TRUE) {
                flag = *(CK_BBOOL *)attr->pValue;

                rc = build_attribute(CKA_NEVER_EXTRACTABLE, &true,
                    sizeof (CK_BBOOL), &new_attr);
                if (rc != CKR_OK) {
                        goto error;
                }
                if (flag == TRUE)
                        *(CK_BBOOL *)new_attr->pValue = false;

                (void) template_update_attribute(priv_key_obj->template,
                    new_attr);

        } else {
                rc = CKR_FUNCTION_FAILED;
                goto error;
        }

        rc = object_mgr_create_final(sess, publ_key_obj, publ_key_handle);
        if (rc != CKR_OK) {
                goto error;
        }
        rc = object_mgr_create_final(sess, priv_key_obj, priv_key_handle);
        if (rc != CKR_OK) {
                (void) object_mgr_destroy_object(sess, *publ_key_handle);
                publ_key_obj = NULL;
                goto error;
        }
        return (rc);

error:
        if (publ_key_obj)
                (void) object_free(publ_key_obj);
        if (priv_key_obj)
                (void) object_free(priv_key_obj);

        *publ_key_handle = 0;
        *priv_key_handle = 0;

        return (rc);
}

CK_RV
key_mgr_wrap_key(SESSION           * sess,
        CK_BBOOL        length_only,
        CK_MECHANISM    * mech,
        CK_OBJECT_HANDLE    h_wrapping_key,
        CK_OBJECT_HANDLE    h_key,
        CK_BYTE   * wrapped_key,
        CK_ULONG  * wrapped_key_len) {
        ENCR_DECR_CONTEXT * ctx = NULL;
        OBJECT    * key1_obj  = NULL;
        OBJECT    * key2_obj  = NULL;
        CK_ATTRIBUTE    * attr  = NULL;
        CK_BYTE   * data        = NULL;
        CK_ULONG    data_len;
        CK_OBJECT_CLASS class;
        CK_KEY_TYPE      keytype;
        CK_BBOOL    flag;
        CK_RV   rc;

        if (! sess || ! wrapped_key_len) {
                return (CKR_FUNCTION_FAILED);
        }

        rc = object_mgr_find_in_map1(sess->hContext, h_wrapping_key, &key1_obj);
        if (rc != CKR_OK) {
                return (CKR_WRAPPING_KEY_HANDLE_INVALID);
        }
        rc = object_mgr_find_in_map1(sess->hContext, h_key, &key2_obj);
        if (rc != CKR_OK) {
                return (CKR_KEY_HANDLE_INVALID);
        }

        rc = template_attribute_find(key2_obj->template,
            CKA_EXTRACTABLE, &attr);
        if (rc == FALSE) {
                return (CKR_KEY_NOT_WRAPPABLE);
        } else {
                flag = *(CK_BBOOL *)attr->pValue;
                if (flag == FALSE) {
                        return (CKR_KEY_NOT_WRAPPABLE);
                }
        }

        rc = template_attribute_find(key2_obj->template, CKA_CLASS, &attr);
        if (rc == FALSE) {
                return (CKR_KEY_NOT_WRAPPABLE);
        } else
                class = *(CK_OBJECT_CLASS *)attr->pValue;

        switch (mech->mechanism) {
                case CKM_RSA_PKCS:
                if (class != CKO_SECRET_KEY) {
                        return (CKR_KEY_NOT_WRAPPABLE);
                }
                break;

                default:
                return (CKR_KEY_NOT_WRAPPABLE);
        }

        rc = template_attribute_find(key2_obj->template,
            CKA_KEY_TYPE, &attr);
        if (rc == FALSE)
                return (CKR_KEY_NOT_WRAPPABLE);
        else
                keytype = *(CK_KEY_TYPE *)attr->pValue;

        switch (keytype) {
                case CKK_RSA:
                rc = rsa_priv_wrap_get_data(key2_obj->template, length_only,
                    &data, &data_len);
                if (rc != CKR_OK) {
                        return (rc);
                }
                break;

                case CKK_GENERIC_SECRET:
                rc = generic_secret_wrap_get_data(key2_obj->template,
                    length_only, &data, &data_len);
                if (rc != CKR_OK) {
                        return (rc);
                }
                break;
                default:
                return (CKR_KEY_NOT_WRAPPABLE);
        }

        switch (mech->mechanism) {
                case CKM_RSA_PKCS:
                break;

                default:
                return (CKR_KEY_NOT_WRAPPABLE);
        }

        ctx = (ENCR_DECR_CONTEXT *)malloc(sizeof (ENCR_DECR_CONTEXT));
        if (! ctx) {
                return (CKR_HOST_MEMORY);
        }
        (void) memset(ctx, 0x0, sizeof (ENCR_DECR_CONTEXT));

        rc = encr_mgr_init(sess, ctx, OP_WRAP, mech, h_wrapping_key);
        if (rc != CKR_OK) {
                return (rc);
        }
        rc = encr_mgr_encrypt(sess,     length_only,
            ctx, data,  data_len, wrapped_key, wrapped_key_len);

        if (data != NULL) {
                free(data);
        }
        (void) encr_mgr_cleanup(ctx);
        free(ctx);

        return (rc);
}

CK_RV
key_mgr_unwrap_key(SESSION         * sess,
        CK_MECHANISM    * mech,
        CK_ATTRIBUTE    * attributes,
        CK_ULONG            attrib_count,
        CK_BYTE    * wrapped_key,
        CK_ULONG            wrapped_key_len,
        CK_OBJECT_HANDLE    h_unwrapping_key,
        CK_OBJECT_HANDLE  * h_unwrapped_key)
{
        ENCR_DECR_CONTEXT * ctx = NULL;
        OBJECT      * key_obj = NULL;
        CK_BYTE    * data = NULL;
        CK_ULONG            data_len;
        CK_ULONG            keyclass, keytype;
        CK_ULONG            i;
        CK_BBOOL            found_class, found_type, fromend;
        CK_RV           rc;


        if (! sess || ! wrapped_key || ! h_unwrapped_key) {
                return (CKR_FUNCTION_FAILED);
        }

        rc = object_mgr_find_in_map1(sess->hContext, h_unwrapping_key,
            &key_obj);
        if (rc != CKR_OK) {
                return (CKR_WRAPPING_KEY_HANDLE_INVALID);
        }

        found_class    = FALSE;
        found_type      = FALSE;

        switch (mech->mechanism) {
                case CKM_RSA_PKCS:
                        keyclass = CKO_SECRET_KEY;
                        found_class = TRUE;
                break;
        }

        for (i = 0; i < attrib_count; i++) {
                switch (attributes[i].type) {
                        case CKA_CLASS:
                        keyclass = *(CK_OBJECT_CLASS *)attributes[i].pValue;
                        found_class = TRUE;
                        break;

                        case CKA_KEY_TYPE:
                        keytype = *(CK_KEY_TYPE *)attributes[i].pValue;
                        found_type = TRUE;
                        break;
                }
        }

        if (found_class == FALSE || (found_type == FALSE && keyclass !=
            CKO_PRIVATE_KEY)) {
                return (CKR_TEMPLATE_INCOMPLETE);
        }

        switch (mech->mechanism) {
                case CKM_RSA_PKCS:
                if (keyclass != CKO_SECRET_KEY) {
                        return (CKR_TEMPLATE_INCONSISTENT);
                }
                break;
                default:
                        return (CKR_MECHANISM_INVALID);
        }


        ctx = (ENCR_DECR_CONTEXT *)malloc(sizeof (ENCR_DECR_CONTEXT));
        if (! ctx) {
                return (CKR_HOST_MEMORY);
        }
        (void) memset(ctx, 0x0, sizeof (ENCR_DECR_CONTEXT));

        rc = decr_mgr_init(sess, ctx, OP_UNWRAP, mech, h_unwrapping_key);
        if (rc != CKR_OK)
                return (rc);

        rc = decr_mgr_decrypt(sess,
            TRUE, ctx, wrapped_key, wrapped_key_len,
            data,       &data_len);
        if (rc != CKR_OK) {
                goto error;
        }
        data = (CK_BYTE *)malloc(data_len);
        if (! data) {
                rc = CKR_HOST_MEMORY;
                goto error;
        }

        rc = decr_mgr_decrypt(sess,
            FALSE, ctx, wrapped_key, wrapped_key_len,
            data,       &data_len);

        (void) decr_mgr_cleanup(ctx);
        free(ctx);

        if (rc != CKR_OK) {
                goto error;
        }
        /*
         * if we use X.509, the data will be padded from the front with zeros.
         * PKCS #11 specifies that for this mechanism, CK_VALUE is to be read
         * from the end of the data.
         *
         * Note: the PKCS #11 reference implementation gets this wrong.
         */
        if (mech->mechanism == CKM_RSA_X_509)
                fromend = TRUE;
        else
        fromend = FALSE;

        if (keyclass == CKO_PRIVATE_KEY) {
                rc = key_mgr_get_private_key_type(data, data_len, &keytype);
                if (rc != CKR_OK) {
                        goto error;
                }
        }

        rc = object_mgr_create_skel(sess,
            attributes,    attrib_count,
            MODE_UNWRAP, keyclass,      keytype,
            &key_obj);
        if (rc != CKR_OK) {
                goto error;
        }
        switch (keyclass) {
                case CKO_SECRET_KEY:
                rc = secret_key_unwrap(key_obj->template, keytype, data,
                    data_len, fromend);
                break;

                case CKO_PRIVATE_KEY:
                rc = priv_key_unwrap(key_obj->template, keytype,
                    data, data_len);
                break;

                default:
                rc = CKR_WRAPPED_KEY_INVALID;
                break;
        }

        if (rc != CKR_OK) {
                goto error;
        }
        rc = object_mgr_create_final(sess, key_obj, h_unwrapped_key);
        if (rc != CKR_OK) {
                goto error;
        }
        if (data) free(data);
        return (rc);

error:
        if (key_obj) (void) object_free(key_obj);
        if (data)    free(data);

        return (rc);
}