From d611032e1e5b4e2261625ee924071e9713320837 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Thu, 4 Dec 2008 18:59:50 +0000 Subject: Implement RSA Sign and Decrypt operations. Not tested. --- ckcapi-der.c | 177 ++++++++++++++++++++++++ ckcapi-der.h | 35 +++++ ckcapi-key.c | 92 +++++++++---- ckcapi-key.h | 11 +- ckcapi-rsa.c | 405 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ ckcapi-rsa.h | 41 ++++++ ckcapi-session.c | 199 +++++++++++++++++++++++++++ ckcapi-session.h | 27 ++-- ckcapi.c | 128 +++++++++++++----- ckcapi.h | 5 + ckcapi.vcproj | 16 +++ 11 files changed, 1063 insertions(+), 73 deletions(-) create mode 100644 ckcapi-der.c create mode 100644 ckcapi-der.h create mode 100644 ckcapi-rsa.c create mode 100644 ckcapi-rsa.h diff --git a/ckcapi-der.c b/ckcapi-der.c new file mode 100644 index 0000000..602b831 --- /dev/null +++ b/ckcapi-der.c @@ -0,0 +1,177 @@ +/* + * Portions derived from NSS source files: + * lib/ckfw/capi/cobject.c + * lib/ckfw/capi/crsa.c + * + * Portions of this file: + * Copyright (C) Stef Walter 2008 + * + */ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * Portions created by Red Hat, Inc, are Copyright (C) 2005 + * + * Contributor(s): + * Bob Relyea (rrelyea@redhat.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "ckcapi.h" +#include "ckcapi-der.h" + +/* + * unwrap a single DER value + */ +BYTE* +ckcapi_der_unwrap(BYTE* src, DWORD n_src, + DWORD* n_result, BYTE** next) +{ + BYTE* start = src; + BYTE* end = src + n_src; + DWORD len = 0; + + /* initialize error condition return values */ + *n_result = 0; + if(next) + *next = src; + + if(n_src < 2) + return start; + + src++ ; /* skip the tag -- should check it against an expected value! */ + len = (DWORD)*src++; + if(len & 0x80) + { + DWORD count = len & 0x7f; + len = 0; + + if(count + 2 > n_src) + return start; + + while(count-- > 0) + len = (len << 8) | (DWORD)*src++; + } + + if(len + (src - start) > (DWORD)n_src) + return start; + + if(next) + *next = src + len; + + *n_result = len; + return src; +} + +/* + * write a Decimal value to a string + */ + +static char* +put_decimal_string(char* cstr, DWORD value) +{ + DWORD tenpower; + BOOL first = TRUE; + + for(tenpower = 10000000; tenpower; tenpower /= 10) + { + BYTE digit = (BYTE)(value / tenpower); + value = value % tenpower; + + /* drop leading zeros */ + if(first && (0 == digit)) + continue; + + first = FALSE; + *cstr++ = digit + '0'; + } + + /* if value was zero, put one of them out */ + if(first) + *cstr++ = '0'; + + return cstr; +} + +/* + * Create a Capi OID string value from a DER OID + */ +char* +ckcapi_der_read_oid(BYTE* oid_tag, DWORD n_oid_tag) +{ + BYTE* oid; + char *oid_str; + char *cstr; + DWORD value; + DWORD n_oid; + + /* wasn't an oid */ + if(CKCAPI_DER_OBJECT_ID != *oid_tag) + return NULL; + + oid = ckcapi_der_unwrap(oid_tag, n_oid_tag, &n_oid, NULL);; + if(n_oid < 2) + return NULL; + + oid_str = malloc(n_oid * 4); + if(!oid_str) + return NULL; + + cstr = oid_str; + cstr = put_decimal_string(cstr, (*oid) / 40); + *cstr++ = '.'; + cstr = put_decimal_string(cstr, (*oid) % 40); + n_oid--; + + value = 0; + while(n_oid--) + { + oid++; + value = (value << 7) + (*oid & 0x7f); + if(0 == (*oid & 0x80)) + { + *cstr++ = '.'; + cstr = put_decimal_string(cstr, value); + value = 0; + } + } + + *cstr = 0; /* NULL terminate */ + + if(value != 0) + { + free(oid_str); + return NULL; + } + + return oid_str; +} diff --git a/ckcapi-der.h b/ckcapi-der.h new file mode 100644 index 0000000..9030ce3 --- /dev/null +++ b/ckcapi-der.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2008 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef CKCAPI_DER_H +#define CKCAPI_DER_H + +#include "ckcapi.h" + +#define CKCAPI_DER_OCTET_STRING 0x04 +#define CKCAPI_DER_OBJECT_ID 0x06 +#define CKCAPI_DER_SEQUENCE 0x10 +#define CKCAPI_DER_CONSTRUCTED 0x20 + +BYTE* ckcapi_der_unwrap (BYTE* src, DWORD n_src, + DWORD* n_result, BYTE** next); + +char* ckcapi_der_read_oid (BYTE* oid_tag, DWORD n_oid_tag); + +#endif /* CRYPTOKI_DER_H */ diff --git a/ckcapi-key.c b/ckcapi-key.c index 1ce057c..88e769e 100644 --- a/ckcapi-key.c +++ b/ckcapi-key.c @@ -97,6 +97,40 @@ typedef struct _KeyObjectData } KeyObjectData; +static CK_RV +load_key_handle (CkCapiObjectData* objdata, HCRYPTKEY* ret_key) +{ + KeyObjectData* kdata = (KeyObjectData*)objdata; + HCRYPTPROV prov; + HCRYPTKEY key; + DWORD error; + + ASSERT(kdata); + + if(!CryptAcquireContextW(&prov, kdata->prov_info->pwszContainerName, + kdata->prov_info->pwszProvName, + kdata->prov_info->dwProvType, 0)) + { + return ckcapi_winerr_to_ckr(GetLastError()); + } + + if(!CryptGetUserKey(prov, kdata->prov_info->dwKeySpec, &key)) + { + error = GetLastError(); + CryptReleaseContext(prov, 0); + return ckcapi_winerr_to_ckr(error); + } + + if(ret_key) + *ret_key = key; + else + CryptDestroyKey(key); + + CryptReleaseContext(prov, 0); + return CKR_OK; +} + + static CK_RV load_raw_public_key(KeyObjectData* kdata) { @@ -108,7 +142,7 @@ load_raw_public_key(KeyObjectData* kdata) ASSERT(kdata); ASSERT(!kdata->raw_public_key.pbData); - ret = ckcapi_key_object_data_get_handles(&kdata->base, NULL, &key); + ret = load_key_handle(&kdata->base, &key); if(ret != CKR_OK) return ret; @@ -994,40 +1028,42 @@ ckcapi_key_find(CkCapiSession* sess, CK_OBJECT_CLASS cls, return ret; } -CK_RV -ckcapi_key_object_data_get_handles (CkCapiObjectData* objdata, HCRYPTPROV* ret_prov, - HCRYPTKEY* ret_key) +DWORD +ckcapi_key_object_data_get_bits(CkCapiObjectData* objdata) { - KeyObjectData* kdata = (KeyObjectData*)objdata; - HCRYPTPROV prov; - HCRYPTKEY key; - DWORD error; + KeyObjectData* kdata; + PUBLICKEYSTRUC* header; + RSAPUBKEY* pubkey; + CK_RV ret; - ASSERT(kdata); + ASSERT(objdata); - if(!CryptAcquireContextW(&prov, kdata->prov_info->pwszContainerName, - kdata->prov_info->pwszProvName, - kdata->prov_info->dwProvType, 0)) + kdata = (KeyObjectData*)objdata; + + if(!kdata->raw_public_key.pbData) { - return ckcapi_winerr_to_ckr(GetLastError()); + ret = load_raw_public_key(kdata); + if(ret != CKR_OK) + return ret; } - if(!CryptGetUserKey(prov, kdata->prov_info->dwKeySpec, &key)) - { - error = GetLastError(); - CryptReleaseContext(prov, 0); - return ckcapi_winerr_to_ckr(error); - } + header = (PUBLICKEYSTRUC*)kdata->raw_public_key.pbData; + if(!header->bType == PUBLICKEYBLOB) + return 0; - if(ret_key) - *ret_key = key; - else - CryptDestroyKey(key); + pubkey = (RSAPUBKEY*)(header + 1); + if(!pubkey->magic == 0x31415352) + return 0; - if(ret_prov) - *ret_prov = prov; - else - CryptReleaseContext(prov, 0); + return pubkey->bitlen; +} - return CKR_OK; +CRYPT_KEY_PROV_INFO* +ckcapi_key_object_data_get_prov_info(CkCapiObjectData* objdata) +{ + KeyObjectData* kdata; + + ASSERT(objdata); + kdata = (KeyObjectData*)objdata; + return kdata->prov_info; } diff --git a/ckcapi-key.h b/ckcapi-key.h index d09b30c..eabe7a9 100644 --- a/ckcapi-key.h +++ b/ckcapi-key.h @@ -23,11 +23,12 @@ #include "ckcapi.h" /* Find key objects matching criteria */ -CK_RV ckcapi_key_find (CkCapiSession* sess, CK_OBJECT_CLASS cls, - CK_ATTRIBUTE_PTR match, CK_ULONG count, - CkCapiArray* arr); +CK_RV ckcapi_key_find (CkCapiSession* sess, CK_OBJECT_CLASS cls, + CK_ATTRIBUTE_PTR match, CK_ULONG count, + CkCapiArray* arr); -CK_RV ckcapi_key_object_data_get_handles (CkCapiObjectData* objdata, HCRYPTPROV* prov, - HCRYPTKEY* key); +DWORD ckcapi_key_object_data_get_bits (CkCapiObjectData* objdata); + +CRYPT_KEY_PROV_INFO* ckcapi_key_object_data_get_prov_info (CkCapiObjectData* objdata); #endif /* CRYPTOKI_KEY_H */ diff --git a/ckcapi-rsa.c b/ckcapi-rsa.c new file mode 100644 index 0000000..0d4e75d --- /dev/null +++ b/ckcapi-rsa.c @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2008 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "ckcapi.h" + +#include "ckcapi-der.h" +#include "ckcapi-key.h" +#include "ckcapi-object.h" + +/* + * Portions derived from NSS source files: + * lib/ckfw/capi/crsa.c + */ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Red Hat, Inc. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Relyea (rrelyea@redhat.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#define SSL3_SHAMD5_HASH_SIZE 36 /* LEN_MD5 (16) + LEN_SHA1 (20) */ + +/* + * PKCS #11 sign for RSA expects to take a fully DER-encoded hash value, + * which includes the hash OID. CAPI expects to take a Hash Context. While + * CAPI does have the capability of setting a raw hash value, it does not + * have the ability to sign an arbitrary value. This function tries to + * reduce the passed in data into something that CAPI could actually sign. + */ +static CK_BYTE_PTR +parse_rsa_pkcs_der_hash(CK_BYTE_PTR input, CK_ULONG n_input, + ALG_ID* algorithm, CK_ULONG_PTR n_hash) +{ + BYTE* algid; + BYTE* oid; + BYTE* hash_data; + BYTE* oid_str; + DWORD n_oid; + DWORD n_algid; + + /* + * there are 2 types of hashes NSS typically tries to sign, regular + * RSA signature format (with encoded DER_OIDS), and SSL3 Signed hashes. + * CAPI knows not to add any oids to SSL3_Signed hashes, so if we have any + * random hash that is exactly the same size as an SSL3 hash, then we can + * just pass the data through. CAPI has know way of knowing if the value + * is really a combined hash or some other arbitrary data, so it's safe to + * handle this case first. + */ + if(SSL3_SHAMD5_HASH_SIZE == n_input) + { + *n_hash = n_input; + *algorithm = CALG_SSL3_SHAMD5; + return input; + } + + /* make sure we have a sequence tag */ + if((CKCAPI_DER_SEQUENCE | CKCAPI_DER_CONSTRUCTED) != *input) + return NULL; + + /* + * parse the input block to get 1) the hash oid, and 2) the raw hash value. + * unfortunatly CAPI doesn't have a builtin function to do this work, so + * we go ahead and do it by hand here. + * + * format is: + * SEQUENCE { + * SECQUENCE { // algid + * OID {} // oid + * ANY {} // optional params + * } + * OCTECT {} // hash + */ + + /* unwrap */ + algid = ckcapi_der_unwrap(input, n_input, &n_algid, NULL); + if(!algid) + return NULL; + + /* make sure there is not extra data at the end */ + if(algid + n_algid != input + n_input) + return NULL; + + /* wasn't an algid */ + if((CKCAPI_DER_SEQUENCE | CKCAPI_DER_CONSTRUCTED) != *algid) + return NULL; + + oid = ckcapi_der_unwrap(algid, n_algid, &n_oid, &hash_data); + if(!oid || !hash_data) + return NULL; + + if(algorithm) + { + /* + * get the real oid as a string. Again, Microsoft does not + * export anything that does this for us + */ + oid_str = ckcapi_der_read_oid(oid, n_oid); + if(!oid_str) + return NULL; + + /* look up the hash alg from the oid (fortunately CAPI does to this) */ + *algorithm = CertOIDToAlgId(oid_str); + free(oid_str); + } + + /* wasn't a hash? */ + if(CKCAPI_DER_OCTET_STRING != *hash_data) + return NULL; + + /* get the real raw hash */ + return ckcapi_der_unwrap(hash_data, n_algid - (hash_data - algid), + n_hash, NULL); +} + +CK_RV +ckcapi_rsa_pkcs_sign_init(CkCapiObjectData *keydata, void** operation) +{ + CRYPT_KEY_PROV_INFO* prov_info; + + ASSERT(keydata); + ASSERT(operation); + ASSERT(!*operation); + + prov_info = ckcapi_key_object_data_get_prov_info(keydata); + if(prov_info->dwProvType == PROV_RSA_FULL) + return CKR_KEY_TYPE_INCONSISTENT; + + *operation = keydata; + return CKR_OK; +} + +CK_RV +ckcapi_rsa_pkcs_sign_perform (CK_BYTE_PTR data, CK_ULONG n_data, + CK_BYTE_PTR signature, CK_ULONG_PTR n_signature, + void** operation) +{ + CRYPT_KEY_PROV_INFO* prov_info; + CkCapiObjectData* keydata; + ALG_ID algorithm; + BYTE* hash_data; + DWORD n_hash_data; + BOOL capifail; + DWORD len, check; + DWORD bits; + CK_RV ret; + + HCRYPTPROV prov = 0; + HCRYPTHASH hash = 0; + + + ASSERT(operation); + ASSERT(*operation); + + if(!data || !n_data) + return CKR_ARGUMENTS_BAD; + + keydata = (CkCapiObjectData*)*operation; + + prov_info = ckcapi_key_object_data_get_prov_info(keydata); + ASSERT(prov_info); + + /* Calculate the number of bits */ + bits = ckcapi_key_object_data_get_bits (keydata); + if(!bits) + return CKR_GENERAL_ERROR; + + /* Want to know the length */ + if(!signature) + { + *n_signature = bits / 8; + return CKR_OK; + } + + /* TODO: Support arbitrary input on Vista */ + + /* + * PKCS #11 sign for RSA expects to take a fully DER-encoded hash value, + * which includes the hash OID. CAPI expects to take a Hash Context. While + * CAPI does have the capability of setting a raw hash value, it does not + * have the ability to sign an arbitrary value. This function tries to + * reduce the passed in data into something that CAPI could actually sign. + */ + hash_data = parse_rsa_pkcs_der_hash(data, n_data, &algorithm, &n_hash_data); + if(!hash_data) + return CKR_DATA_INVALID; + + capifail = TRUE; + if(CryptAcquireContextW(&prov, prov_info->pwszContainerName, prov_info->pwszProvName, + prov_info->dwProvType, 0)) + { + if(CryptCreateHash(prov, algorithm, 0, 0, &hash)) + { + /* make sure the hash lens match before we set it */ + len = sizeof(DWORD); + if(CryptGetHashParam(hash, HP_HASHSIZE, (BYTE*)&check, &len, 0)) + { + if(check != n_hash_data) + { + capifail = FALSE; + ret = CKR_DATA_INVALID; + } + + /* + * we have an explicit hash, set it, note that the length is + * implicit by the hashAlg used in create + */ + if(CryptSetHashParam(hash, HP_HASHVAL, hash_data, 0)) + { + /* OK, we have the data in a hash structure, sign it! */ + if(CryptSignHash(hash, prov_info->dwKeySpec, + NULL, 0, signature, n_signature)) + { + /* + * OK, Microsoft likes to do things completely + * differently than anyone else. We need to reverse + * the data we recieved here + */ + if(signature) + ckcapi_reverse_memory(signature, *n_signature); + + capifail = FALSE; + ret = CKR_OK; + } + } + } + } + } + + if(capifail) + ret = ckcapi_winerr_to_ckr(GetLastError()); + + if(hash) + CryptDestroyHash(hash); + if(prov) + CryptReleaseContext(prov, 0); + + return ret; +} + +void +ckcapi_rsa_pkcs_sign_cleanup (void* operation) +{ + /* Nothing to do */ +} + + +CK_RV +ckcapi_rsa_pkcs_decrypt_init(CkCapiObjectData* keydata, void** operation) +{ + CRYPT_KEY_PROV_INFO* prov_info; + + ASSERT(keydata); + ASSERT(operation); + ASSERT(!*operation); + + prov_info = ckcapi_key_object_data_get_prov_info(keydata); + if(prov_info->dwProvType == PROV_RSA_FULL) + return CKR_KEY_TYPE_INCONSISTENT; + + *operation = keydata; + return CKR_OK; +} + +CK_RV +ckcapi_rsa_pkcs_decrypt_perform(CK_BYTE_PTR encdata, CK_ULONG n_encdata, + CK_BYTE_PTR result, CK_ULONG_PTR n_result, + void** operation) +{ + CRYPT_KEY_PROV_INFO* prov_info; + CkCapiObjectData* keydata; + BOOL capifail; + DWORD bits, error; + CK_RV ret; + + HCRYPTPROV prov = 0; + HCRYPTKEY key = 0; + void* buffer = NULL; + + ASSERT(operation); + ASSERT(*operation); + ASSERT(encdata); + ASSERT(n_encdata); + + keydata = (CkCapiObjectData*)*operation; + + prov_info = ckcapi_key_object_data_get_prov_info(keydata); + ASSERT(prov_info); + + /* Calculate the number of bits */ + bits = ckcapi_key_object_data_get_bits (keydata); + if(!bits) + return CKR_GENERAL_ERROR; + + /* Want to know the length */ + if(!result) + { + *n_result = bits / 8; + return CKR_OK; + } + + /* + * Copy the input, since CAPI operates in place, and + * we must also reverse it properly. + */ + buffer = malloc(n_encdata); + if(!buffer) + return CKR_HOST_MEMORY; + + memcpy(buffer, encdata, n_encdata); + ckcapi_reverse_memory(buffer, n_encdata); + + capifail = TRUE; + if(CryptAcquireContextW(&prov, prov_info->pwszContainerName, prov_info->pwszProvName, + prov_info->dwProvType, 0)) + { + if(CryptGetUserKey(prov, prov_info->dwKeySpec, &key)) + { + *n_result = n_encdata; + if(CryptDecrypt(key, 0, TRUE, 0, buffer, n_result)) + { + capifail = FALSE; + ret = CKR_OK; + } + } + } + + if(capifail) + { + error = GetLastError(); + switch(error) + { + case NTE_BAD_DATA: + ret = CKR_ENCRYPTED_DATA_INVALID; + default: + ret = ckcapi_winerr_to_ckr(error); + }; + } + + /* Copy the memory out to the result buffer */ + if(ret == CKR_OK) + ret = ckcapi_return_data_raw(result, n_result, buffer, *n_result); + + if(key) + CryptDestroyKey(key); + if(prov) + CryptReleaseContext(prov, 0); + if(buffer) + free(buffer); + + return ret; +} + +void +ckcapi_rsa_pkcs_decrypt_cleanup(void* operation) +{ + /* Nothing to do */ +} diff --git a/ckcapi-rsa.h b/ckcapi-rsa.h new file mode 100644 index 0000000..4e00133 --- /dev/null +++ b/ckcapi-rsa.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2008 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef CKCAPI_RSA_H +#define CKCAPI_RSA_H + +#include "ckcapi.h" + +CK_RV ckcapi_rsa_pkcs_sign_init (CkCapiObjectData* keydata, void** operation); + +CK_RV ckcapi_rsa_pkcs_sign_perform (CK_BYTE_PTR data, CK_ULONG data_len, + CK_BYTE_PTR signature, CK_ULONG_PTR signature_len, + void** operation); + +void ckcapi_rsa_pkcs_sign_cleanup (void* operation); + +CK_RV ckcapi_rsa_pkcs_decrypt_init (CkCapiObjectData* keydata, void** operation); + +CK_RV ckcapi_rsa_pkcs_decrypt_perform (CK_BYTE_PTR encdata, CK_ULONG n_encdata, + CK_BYTE_PTR result, CK_ULONG_PTR n_result, + void** operation); + +void ckcapi_rsa_pkcs_decrypt_cleanup (void* operation); + +#endif /* CKCAPI_RSA_H */ diff --git a/ckcapi-session.c b/ckcapi-session.c index 46de60a..0745cb7 100644 --- a/ckcapi-session.c +++ b/ckcapi-session.c @@ -23,10 +23,20 @@ #include "ckcapi-builtin.h" #include "ckcapi-cert.h" #include "ckcapi-object.h" +#include "ckcapi-rsa.h" #include "ckcapi-session.h" #include "ckcapi-token.h" #include "ckcapi-trust.h" +/* For operation_type in CkCapiSession */ +enum +{ + OPERATION_NONE, + OPERATION_FIND, + OPERATION_SIGN, + OPERATION_DECRYPT +}; + static CkCapiArray* all_sessions = NULL; static void @@ -721,3 +731,192 @@ ckcapi_session_find_final(CkCapiSession* sess) } +/* ---------------------------------------------------------------------------- + * CRYPTO OPERATIONS + */ + +typedef struct _CryptoContext +{ + CK_MECHANISM_TYPE mech_type; + CkCapiDestroyFunc mech_cleanup; + void* mech_data; +} +CryptoContext; + +void +cleanup_crypto_operation(CkCapiSession* sess) +{ + CryptoContext* ctx; + + if(sess->operation_data) + { + ctx = (CryptoContext*)sess->operation_data; + if(ctx->mech_cleanup) + (ctx->mech_cleanup)(ctx->mech_data); + free(ctx); + } + + sess->operation_type = OPERATION_NONE; + sess->operation_data = NULL; + sess->operation_cancel = NULL; +} + +CK_RV +ckcapi_session_sign_init(CkCapiSession* sess, CK_MECHANISM_PTR mech, + CkCapiObjectData *objdata) +{ + CryptoContext* ctx; + CK_RV ret; + + ASSERT(sess); + ASSERT(mech); + ASSERT(objdata); + + if(sess->operation_type != OPERATION_NONE) + return CKR_OPERATION_ACTIVE; + + ctx = calloc(1, sizeof(CryptoContext)); + if(!ctx) + return CKR_HOST_MEMORY; + + ctx->mech_type = mech->mechanism; + + switch(mech->mechanism) + { + case CKM_RSA_PKCS: + ret = ckcapi_rsa_pkcs_sign_init(objdata, &ctx->mech_data); + ctx->mech_cleanup = ckcapi_rsa_pkcs_sign_cleanup; + default: + ret = CKR_MECHANISM_INVALID; + }; + + if(ret != CKR_OK) + { + free(ctx); + ASSERT(!sess->operation_data); + return ret; + } + + sess->operation_type = OPERATION_SIGN; + sess->operation_data = ctx; + sess->operation_cancel = cleanup_crypto_operation; + return CKR_OK; +} + +CK_RV +ckcapi_session_sign(CkCapiSession* sess, CK_BYTE_PTR data, CK_ULONG n_data, + CK_BYTE_PTR signature, CK_ULONG_PTR n_signature) +{ + CryptoContext *ctx; + BOOL incomplete; + CK_RV ret; + + ASSERT(sess); + ASSERT(data); + ASSERT(n_data); + + if(sess->operation_type != OPERATION_SIGN) + return CKR_OPERATION_NOT_INITIALIZED; + + ctx = (CryptoContext*)sess->operation_data; + switch(ctx->mech_type) + { + case CKM_RSA_PKCS: + ret = ckcapi_rsa_pkcs_sign_perform(data, n_data, signature, n_signature, + &ctx->mech_data); + break; + + default: + ASSERT(FALSE); + ret = CKR_GENERAL_ERROR; + break; + } + + /* Buffer calculation, we don't end operation */ + incomplete = (ret == CKR_BUFFER_TOO_SMALL || (ret == CKR_OK && !signature)); + + if(!incomplete) + cleanup_crypto_operation(sess); + + return ret; +} + +CK_RV +ckcapi_session_decrypt_init(CkCapiSession* sess, CK_MECHANISM_PTR mech, + CkCapiObjectData *objdata) +{ + CryptoContext* ctx; + CK_RV ret; + + ASSERT(sess); + ASSERT(mech); + ASSERT(objdata); + + if(sess->operation_type != OPERATION_NONE) + return CKR_OPERATION_ACTIVE; + + ctx = calloc(1, sizeof(CryptoContext)); + if(!ctx) + return CKR_HOST_MEMORY; + + ctx->mech_type = mech->mechanism; + + switch(mech->mechanism) + { + case CKM_RSA_PKCS: + ret = ckcapi_rsa_pkcs_decrypt_init(objdata, &ctx->mech_data); + ctx->mech_cleanup = ckcapi_rsa_pkcs_decrypt_cleanup; + default: + ret = CKR_MECHANISM_INVALID; + }; + + if(ret != CKR_OK) + { + free(ctx); + ASSERT(!sess->operation_data); + return ret; + } + + sess->operation_type = OPERATION_DECRYPT; + sess->operation_data = ctx; + sess->operation_cancel = cleanup_crypto_operation; + return CKR_OK; +} + +CK_RV +ckcapi_session_decrypt(CkCapiSession* sess, CK_BYTE_PTR encdata, CK_ULONG n_encdata, + CK_BYTE_PTR result, CK_ULONG_PTR n_result) +{ + CryptoContext *ctx; + BOOL incomplete; + CK_RV ret; + + ASSERT(sess); + ASSERT(encdata); + ASSERT(n_encdata); + + if(sess->operation_type != OPERATION_DECRYPT) + return CKR_OPERATION_NOT_INITIALIZED; + + ctx = (CryptoContext*)sess->operation_data; + switch(ctx->mech_type) + { + case CKM_RSA_PKCS: + ret = ckcapi_rsa_pkcs_decrypt_perform(encdata, n_encdata, result, n_result, + &ctx->mech_data); + break; + + default: + ASSERT(FALSE); + ret = CKR_GENERAL_ERROR; + break; + } + + /* Buffer calculation, we don't end operation */ + incomplete = (ret == CKR_BUFFER_TOO_SMALL || (ret == CKR_OK && !result)); + + if(!incomplete) + cleanup_crypto_operation(sess); + + return ret; +} diff --git a/ckcapi-session.h b/ckcapi-session.h index 6007662..4fb68fa 100644 --- a/ckcapi-session.h +++ b/ckcapi-session.h @@ -22,14 +22,7 @@ #include "ckcapi.h" -/* For operation_type in CkCapiSession */ -enum -{ - OPERATION_NONE = 0, - OPERATION_FIND = 1, -}; - -/* Callback to cancel a current operation */ +/* Callback to cleanup a current operation */ typedef void (*CkCapiSessionCancel) (struct _CkCapiSession* sess); /* Represents an open session */ @@ -95,7 +88,25 @@ CK_RV ckcapi_session_find (CkCapiSession* sess, CK_RV ckcapi_session_find_final (CkCapiSession* sess); +/* Start a sign operation on a session */ +CK_RV ckcapi_session_sign_init (CkCapiSession* sess, + CK_MECHANISM_PTR mech, + CkCapiObjectData *objdata); + +/* Perform sign operation */ +CK_RV ckcapi_session_sign (CkCapiSession* sess, + CK_BYTE_PTR data, CK_ULONG n_data, + CK_BYTE_PTR sig, CK_ULONG_PTR n_sig); + +/* Start a decrypt operation on a session */ +CK_RV ckcapi_session_decrypt_init (CkCapiSession* sess, + CK_MECHANISM_PTR mech, + CkCapiObjectData *objdata); +/* Perform decrypt operation */ +CK_RV ckcapi_session_decrypt (CkCapiSession* sess, + CK_BYTE_PTR encdata, CK_ULONG n_encdata, + CK_BYTE_PTR result, CK_ULONG_PTR n_result); /* Get object data for an object */ CK_RV ckcapi_session_get_object_data (CkCapiSession* sess, diff --git a/ckcapi.c b/ckcapi.c index 75c133a..b032c13 100644 --- a/ckcapi.c +++ b/ckcapi.c @@ -138,27 +138,38 @@ ckcapi_winerr_to_ckr(DWORD werr) } CK_RV -ckcapi_return_data(CK_ATTRIBUTE_PTR attr, CK_VOID_PTR src, DWORD slen) +ckcapi_return_data_raw(CK_VOID_PTR output, CK_ULONG_PTR n_output, + CK_VOID_PTR input, CK_ULONG n_input) { + ASSERT(n_output); + ASSERT(input); + /* Just asking for the length */ - if(!attr->pValue) + if(!output) { - attr->ulValueLen = slen; + *n_output = n_input; return CKR_OK; } /* Buffer is too short */ - if(slen > attr->ulValueLen) + if(n_input > *n_output) { - attr->ulValueLen = slen; + *n_output = n_input; return CKR_BUFFER_TOO_SMALL; } - attr->ulValueLen = slen; - memcpy(attr->pValue, src, slen); + *n_output = n_input; + memcpy(output, input, n_input); return CKR_OK; } +CK_RV +ckcapi_return_data(CK_ATTRIBUTE_PTR attr, CK_VOID_PTR input, DWORD n_input) +{ + return ckcapi_return_data_raw(attr->pValue, &(attr->ulValueLen), + input, n_input); +} + CK_RV ckcapi_return_string(CK_ATTRIBUTE_PTR attr, WCHAR* string) { @@ -866,7 +877,7 @@ CC_C_EncryptUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, ENTER(C_EncryptUpdate); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - /* TODO: See if we need to implement this */ + /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } @@ -877,7 +888,7 @@ CC_C_EncryptFinal(CK_SESSION_HANDLE session, CK_BYTE_PTR last_encrypted_part, ENTER(C_EncryptFinal); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - /* TODO: See if we need to implement this */ + /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } @@ -885,22 +896,49 @@ static CK_RV CC_C_DecryptInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) { + CkCapiObjectData* objdata; + CkCapiSession* sess; + CK_RV ret; + ENTER(C_DecryptInit); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + PREREQ(mechanism, CKR_ARGUMENTS_BAD); + PREREQ(key, CKR_ARGUMENTS_BAD); - /* TODO: Implement this */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); + ret = ckcapi_session_get_lock_ref(session, 0, &sess); + if(ret == CKR_OK) + { + ret = ckcapi_session_get_object_data_for(sess, key, &objdata); + if(ret == CKR_OK) + ret = ckcapi_session_decrypt_init(sess, mechanism, objdata); + + ckcapi_session_unref_unlock(sess); + } + + RETURN(ret); } static CK_RV CC_C_Decrypt(CK_SESSION_HANDLE session, CK_BYTE_PTR encrypted_data, CK_ULONG encrypted_data_len, CK_BYTE_PTR data, CK_ULONG_PTR data_len) { + CkCapiSession* sess; + CK_RV ret; + ENTER(C_Decrypt); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + PREREQ(encrypted_data, CKR_ARGUMENTS_BAD); + PREREQ(encrypted_data_len, CKR_ARGUMENTS_BAD); - /* TODO: Implement this */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); + ret = ckcapi_session_get_lock_ref(session, 0, &sess); + if(ret == CKR_OK) + { + ret = ckcapi_session_decrypt(sess, encrypted_data, encrypted_data_len, + data, data_len); + ckcapi_session_unref_unlock(sess); + } + + RETURN(ret); } static CK_RV @@ -910,7 +948,7 @@ CC_C_DecryptUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR encrypted_part, ENTER(C_DecryptUpdate); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - /* TODO: Implement this */ + /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } @@ -921,7 +959,7 @@ CC_C_DecryptFinal(CK_SESSION_HANDLE session, CK_BYTE_PTR pLastPart, ENTER(C_DecryptFinal); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - /* TODO: Implement this */ + /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } @@ -931,7 +969,7 @@ CC_C_DigestInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism) ENTER(C_DigestInit); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - /* TODO: Implement this */ + /* RSA/DSA mechs don't support digest. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } @@ -942,7 +980,7 @@ CC_C_Digest(CK_SESSION_HANDLE session, CK_BYTE_PTR data, CK_ULONG data_len, ENTER(C_Digest); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - /* TODO: See if we need to implement this */ + /* RSA/DSA mechs don't support digest. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } @@ -952,7 +990,7 @@ CC_C_DigestUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len ENTER(C_DigestUpdate); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - /* TODO: See if we need to implement this */ + /* RSA/DSA mechs don't support digest. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } @@ -962,7 +1000,7 @@ CC_C_DigestKey(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key) ENTER(C_DigestKey); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - /* TODO: See if we need to implement this */ + /* RSA/DSA mechs don't support digest. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } @@ -973,7 +1011,7 @@ CC_C_DigestFinal(CK_SESSION_HANDLE session, CK_BYTE_PTR digest, ENTER(C_DigestFinal); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - /* TODO: See if we need to implement this */ + /* RSA/DSA mechs don't support digest. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } @@ -981,22 +1019,48 @@ static CK_RV CC_C_SignInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) { + CkCapiObjectData* objdata; + CkCapiSession* sess; + CK_RV ret; + ENTER(C_SignInit); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + PREREQ(mechanism, CKR_ARGUMENTS_BAD); + PREREQ(key, CKR_ARGUMENTS_BAD); - /* TODO: Implement this */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); + ret = ckcapi_session_get_lock_ref(session, 0, &sess); + if(ret == CKR_OK) + { + ret = ckcapi_session_get_object_data_for(sess, key, &objdata); + if(ret == CKR_OK) + ret = ckcapi_session_sign_init(sess, mechanism, objdata); + + ckcapi_session_unref_unlock(sess); + } + + RETURN(ret); } static CK_RV CC_C_Sign(CK_SESSION_HANDLE session, CK_BYTE_PTR data, CK_ULONG data_len, CK_BYTE_PTR signature, CK_ULONG_PTR signature_len) { + CkCapiSession* sess; + CK_RV ret; + ENTER(C_Sign); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + PREREQ(data, CKR_ARGUMENTS_BAD); + PREREQ(data_len, CKR_ARGUMENTS_BAD); - /* TODO: Implement this */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); + ret = ckcapi_session_get_lock_ref(session, 0, &sess); + if(ret == CKR_OK) + { + ret = ckcapi_session_sign(sess, data, data_len, signature, signature_len); + ckcapi_session_unref_unlock(sess); + } + + RETURN(ret); } static CK_RV @@ -1005,7 +1069,7 @@ CC_C_SignUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len) ENTER(C_SignUpdate); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - /* TODO: Implement this */ + /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } @@ -1016,7 +1080,7 @@ CC_C_SignFinal(CK_SESSION_HANDLE session, CK_BYTE_PTR signature, ENTER(C_SignFinal); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - /* TODO: Implement this */ + /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } @@ -1070,7 +1134,7 @@ CC_C_VerifyUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len ENTER(C_VerifyUpdate); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - /* TODO: See if we need to implement this */ + /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } @@ -1081,7 +1145,7 @@ CC_C_VerifyFinal(CK_SESSION_HANDLE session, CK_BYTE_PTR signature, ENTER(C_VerifyFinal); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - /* TODO: See if we need to implement this */ + /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } @@ -1115,7 +1179,7 @@ CC_C_DigestEncryptUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, ENTER(C_DigestEncryptUpdate); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - /* TODO: See if we need to implement this */ + /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } @@ -1127,7 +1191,7 @@ CC_C_DecryptDigestUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR encrypted_part, ENTER(C_DecryptDigestUpdate); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - /* TODO: See if we need to implement this */ + /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } @@ -1139,7 +1203,7 @@ CC_C_SignEncryptUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, ENTER(C_SignEncryptUpdate); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - /* TODO: See if we need to implement this */ + /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } @@ -1151,7 +1215,7 @@ CC_C_DecryptVerifyUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR encrypted_part, ENTER(C_DecryptVerifyUpdate); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - /* TODO: See if we need to implement this */ + /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } diff --git a/ckcapi.h b/ckcapi.h index 18f304f..54929bc 100644 --- a/ckcapi.h +++ b/ckcapi.h @@ -91,6 +91,9 @@ CK_RV ckcapi_winerr_to_ckr (DWORD werr); CK_RV ckcapi_return_data (CK_ATTRIBUTE_PTR attr, CK_VOID_PTR src, DWORD slen); +CK_RV ckcapi_return_data_raw (CK_VOID_PTR output, CK_ULONG_PTR n_output, + CK_VOID_PTR input, CK_ULONG n_input); + /* * This stores a string in the output buffer with appropriate * PKCS#11 codes when the buffer is too short, or the caller @@ -107,6 +110,8 @@ CK_RV ckcapi_return_reversed_data (CK_ATTRIBUTE_PTR attr, /* ------------------------------------------------------------------ */ +typedef void (*CkCapiDestroyFunc)(void* data); + #ifndef ASSERT #include "assert.h" #define ASSERT assert diff --git a/ckcapi.vcproj b/ckcapi.vcproj index 5abc307..8ca8b36 100644 --- a/ckcapi.vcproj +++ b/ckcapi.vcproj @@ -251,6 +251,10 @@ /> + + @@ -275,6 +279,10 @@ /> + + @@ -384,6 +392,10 @@ RelativePath=".\ckcapi-cert.h" > + + @@ -392,6 +404,10 @@ RelativePath=".\ckcapi-object.h" > + + -- cgit v1.2.3