From 5409801c7195dc38665ec4e29c233bf1e9ea53e7 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Fri, 22 May 2009 17:56:11 -0400 Subject: Initial import of code. This is equivalent to the 20080124 tarball in the Fedora nss package tree. --- anchor.c | 50 +++ ckpem.h | 239 ++++++++++++ ckpemver.c | 59 +++ constants.c | 77 ++++ nssckbi.h | 90 +++++ nsspem.h | 75 ++++ pargs.c | 162 ++++++++ pfind.c | 588 ++++++++++++++++++++++++++++++ pinst.c | 602 ++++++++++++++++++++++++++++++ pobject.c | 1175 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ prsa.c | 615 +++++++++++++++++++++++++++++++ psession.c | 452 +++++++++++++++++++++++ pslot.c | 184 ++++++++++ ptoken.c | 333 +++++++++++++++++ rsawrapr.c | 924 ++++++++++++++++++++++++++++++++++++++++++++++ util.c | 288 +++++++++++++++ 16 files changed, 5913 insertions(+) create mode 100644 anchor.c create mode 100644 ckpem.h create mode 100644 ckpemver.c create mode 100644 constants.c create mode 100644 nssckbi.h create mode 100644 nsspem.h create mode 100644 pargs.c create mode 100644 pfind.c create mode 100644 pinst.c create mode 100644 pobject.c create mode 100644 prsa.c create mode 100644 psession.c create mode 100644 pslot.c create mode 100644 ptoken.c create mode 100644 rsawrapr.c create mode 100644 util.c diff --git a/anchor.c b/anchor.c new file mode 100644 index 0000000..621f919 --- /dev/null +++ b/anchor.c @@ -0,0 +1,50 @@ +/* ***** 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. + * + * Contributor(s): + * Rob Crittenden (rcritten@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 ***** */ + +/* + * anchor.c + * + * This file "anchors" the actual cryptoki entry points in this module's + * shared library, which is required for dynamic loading. See the + * comments in nssck.api for more information. + */ + +#include "ckpem.h" + +#define MODULE_NAME pem +#define INSTANCE_NAME (NSSCKMDInstance *)&pem_mdInstance +#include "nssck.api" diff --git a/ckpem.h b/ckpem.h new file mode 100644 index 0000000..5587333 --- /dev/null +++ b/ckpem.h @@ -0,0 +1,239 @@ +#include "nssckmdt.h" +#include "nssckfw.h" +#include "ckfwtm.h" +#include "ckfw.h" +#include "secder.h" +#include "secoid.h" +#include "secasn1.h" +#include "blapit.h" +#include "softoken.h" + +/* + * I'm including this for access to the arena functions. + * Looks like we should publish that API. + */ +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +/* + * This is where the Netscape extensions live, at least for now. + */ +#ifndef CKT_H +#include "ckt.h" +#endif /* CKT_H */ + +#define NUM_SLOTS 8 + +/* + * statically defined raw objects. Allows us to data description objects + * to this PKCS #11 module. + */ +struct pemRawObjectStr { + CK_ULONG n; + const CK_ATTRIBUTE_TYPE *types; + const NSSItem *items; +}; +typedef struct pemRawObjectStr pemRawObject; + +/* + * common values needed for both bare keys and cert referenced keys. + */ +struct pemKeyParamsStr { + NSSItem modulus; + NSSItem exponent; + NSSItem privateExponent; + NSSItem prime1; + NSSItem prime2; + NSSItem exponent1; + NSSItem exponent2; + NSSItem coefficient; + unsigned char publicExponentData[sizeof(CK_ULONG)]; + SECItem *privateKey; + void *pubKey; +}; +typedef struct pemKeyParamsStr pemKeyParams; +/* + * Key objects. Handles bare keys which do not yet have certs associated + * with them. These are usually short lived, but may exist for several days + * while the CA is issuing the certificate. + */ +struct pemKeyObjectStr { + char *provName; + char *containerName; + pemKeyParams key; + char *ivstring; + int cipher; +}; +typedef struct pemKeyObjectStr pemKeyObject; + +/* + * Certificate and certificate referenced keys. + */ +struct pemCertObjectStr { + const char *certStore; + NSSItem label; + NSSItem subject; + NSSItem issuer; + NSSItem serial; + NSSItem derCert; + unsigned char sha1_hash[SHA1_LENGTH]; + unsigned char md5_hash[MD5_LENGTH]; + pemKeyParams key; + unsigned char *labelData; + /* static data: to do, make this dynamic like labelData */ + unsigned char derSerial[128]; +}; +typedef struct pemCertObjectStr pemCertObject; + +/* + * Trust + */ +struct pemTrustObjectStr { + char *nickname; +}; +typedef struct pemTrustObjectStr pemTrustObject; + +typedef enum { + pemRaw, + pemCert, + pemBareKey, + pemTrust +} pemObjectType; + +/* + * all the various types of objects are abstracted away in cobject and + * cfind as pemInternalObjects. + */ +struct pemInternalObjectStr { + pemObjectType type; + union { + pemRawObject raw; + pemCertObject cert; + pemKeyObject key; + pemTrustObject trust; + } u; + CK_OBJECT_CLASS objClass; + NSSItem hashKey; + NSSItem id; + void *idData; + unsigned char hashKeyData[128]; + SECItem *derCert; + char *nickname; + NSSCKMDObject mdObject; + CK_SLOT_ID slotID; +}; +typedef struct pemInternalObjectStr pemInternalObject; + +struct pemTokenStr { + PRBool logged_in; +}; +typedef struct pemTokenStr pemToken; + +/* our raw object data array */ +NSS_EXTERN_DATA pemInternalObject nss_pem_data[]; +NSS_EXTERN_DATA const PRUint32 nss_pem_nObjects; + + PRBool logged_in; + +/* our raw object data array */ +NSS_EXTERN_DATA pemInternalObject nss_pem_data[]; +NSS_EXTERN_DATA const PRUint32 nss_pem_nObjects; + +NSS_EXTERN_DATA pemInternalObject pem_data[]; +NSS_EXTERN_DATA const PRUint32 pem_nObjects; + +NSS_EXTERN_DATA const CK_VERSION pem_CryptokiVersion; +NSS_EXTERN_DATA const NSSUTF8 * pem_ManufacturerID; +NSS_EXTERN_DATA const NSSUTF8 * pem_LibraryDescription; +NSS_EXTERN_DATA const CK_VERSION pem_LibraryVersion; +NSS_EXTERN_DATA const NSSUTF8 * pem_SlotDescription; +NSS_EXTERN_DATA const CK_VERSION pem_HardwareVersion; +NSS_EXTERN_DATA const CK_VERSION pem_FirmwareVersion; +NSS_EXTERN_DATA const NSSUTF8 * pem_TokenLabel; +NSS_EXTERN_DATA const NSSUTF8 * pem_TokenModel; +NSS_EXTERN_DATA const NSSUTF8 * pem_TokenSerialNumber; + +NSS_EXTERN_DATA const NSSCKMDInstance pem_mdInstance; +NSS_EXTERN_DATA const NSSCKMDSlot pem_mdSlot; +NSS_EXTERN_DATA const NSSCKMDToken pem_mdToken; +NSS_EXTERN_DATA const NSSCKMDMechanism pem_mdMechanismRSA; + +NSS_EXTERN NSSCKMDSession * +pem_CreateSession +( + NSSCKFWSession *fwSession, + CK_RV *pError +); + +NSS_EXTERN NSSCKMDFindObjects * +pem_FindObjectsInit +( + NSSCKFWSession *fwSession, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_RV *pError +); + +NSS_EXTERN NSSCKMDObject * +pem_CreateMDObject +( + NSSArena *arena, + pemInternalObject *io, + CK_RV *pError +); + +#define NSS_PEM_ARRAY_SIZE(x) ((sizeof (x))/(sizeof ((x)[0]))) + +typedef enum { + pemLOWKEYNullKey = 0, + pemLOWKEYRSAKey = 1, + pemLOWKEYDSAKey = 2, + pemLOWKEYDHKey = 4, + pemLOWKEYECKey = 5 +} pemLOWKEYType; + +/* +** Low Level private key object +** This is only used by the raw Crypto engines (crypto), keydb (keydb), +** and PKCS #11. Everyone else uses the high level key structure. +*/ +struct pemLOWKEYPrivateKeyStr { + PLArenaPool *arena; + pemLOWKEYType keyType; + union { + RSAPrivateKey rsa; + DSAPrivateKey dsa; + DHPrivateKey dh; + ECPrivateKey ec; + } u; +}; +typedef struct pemLOWKEYPrivateKeyStr pemLOWKEYPrivateKey; + +SECStatus ReadDERFromFile(SECItem ***derlist, char *filename, PRBool ascii, int *cipher, char **ivstring, PRBool certsonly); +const NSSItem * pem_FetchAttribute ( pemInternalObject *io, CK_ATTRIBUTE_TYPE type); +void pem_PopulateModulusExponent(pemInternalObject *io); +NSSCKMDObject * pem_CreateObject(NSSCKFWInstance *fwInstance, NSSCKFWSession *fwSession, NSSCKMDToken *mdToken, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_RV *pError); +NSSCKMDSlot *pem_NewSlot( NSSCKFWInstance *fwInstance, CK_RV *pError); + + +PRBool pem_ParseString(const char* inputstring, const char delimiter, + PRInt32* numStrings, char*** returnedstrings); +PRBool pem_FreeParsedStrings(PRInt32 numStrings, char** instrings); + +pemInternalObject * +CreateObject(CK_OBJECT_CLASS objClass, pemObjectType type, SECItem *certDER, + SECItem *keyDER, char *filename, int objid, CK_SLOT_ID slotID); + + +/* prsa.c */ +unsigned int pem_PrivateModulusLen(pemLOWKEYPrivateKey *privk); + +/* ptoken.c */ +NSSCKMDToken * pem_NewToken(NSSCKFWInstance *fwInstance, CK_RV *pError); + +void open_log(); +void close_log(); +void plog(const char *fmt, ...); + +#define PEM_H 1 diff --git a/ckpemver.c b/ckpemver.c new file mode 100644 index 0000000..76ab5df --- /dev/null +++ b/ckpemver.c @@ -0,0 +1,59 @@ +/* ***** 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): + * Rob Crittenden (rcritten@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 ***** */ +/* Library identity and versioning */ + +#include "nsspem.h" + +#if defined(DEBUG) +#define _DEBUG_STRING " (debug)" +#else +#define _DEBUG_STRING "" +#endif + +/* + * Version information for the 'ident' and 'what commands + * + * NOTE: the first component of the concatenated rcsid string + * must not end in a '$' to prevent rcs keyword substitution. + */ +const char __nss_ckpem_rcsid[] = "$Header: NSS Access to Flat Files in PEM format" + NSS_CKPEM_LIBRARY_VERSION _DEBUG_STRING + " " __DATE__ " " __TIME__ " $"; +const char __nss_ckcapi_sccsid[] = "@(#)NSS Access to Flag Files in PEM format " + NSS_CKPEM_LIBRARY_VERSION _DEBUG_STRING + " " __DATE__ " " __TIME__; diff --git a/constants.c b/constants.c new file mode 100644 index 0000000..d030867 --- /dev/null +++ b/constants.c @@ -0,0 +1,77 @@ +/* ***** 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. + * + * Contributor(s): + * Rob Crittenden (rcritten@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 ***** */ +/* + * constants.c + * + * Identification and other constants, all collected here in one place. + */ + +#ifndef NSSBASET_H +#include "nssbaset.h" +#endif /* NSSBASET_H */ + +#ifndef NSSCKT_H +#include "nssckt.h" +#endif /* NSSCKT_H */ + +#ifndef NSSCKBI_H +#include "nssckbi.h" +#endif /* NSSCKBI_H */ + +NSS_IMPLEMENT_DATA const CK_VERSION +pem_CryptokiVersion = { 2, 1 }; + +NSS_IMPLEMENT_DATA const NSSUTF8 * +pem_ManufacturerID = (NSSUTF8 *) "Red Hat, Inc."; + +NSS_IMPLEMENT_DATA const NSSUTF8 * +pem_LibraryDescription = (NSSUTF8 *) "PEM Reader Cryptoki Module"; + +NSS_IMPLEMENT_DATA const CK_VERSION +pem_LibraryVersion = { 1, 0 }; + +NSS_IMPLEMENT_DATA const CK_VERSION +pem_HardwareVersion = { 1, 0 }; + +NSS_IMPLEMENT_DATA const CK_VERSION +pem_FirmwareVersion = { 1, 0 }; + +NSS_IMPLEMENT_DATA const NSSUTF8 * +pem_TokenModel = (NSSUTF8 *) "1"; + +NSS_IMPLEMENT_DATA const NSSUTF8 * +pem_TokenSerialNumber = (NSSUTF8 *) "1"; diff --git a/nssckbi.h b/nssckbi.h new file mode 100644 index 0000000..9397c27 --- /dev/null +++ b/nssckbi.h @@ -0,0 +1,90 @@ +/* ***** 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. + * + * Contributor(s): + * + * 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 ***** */ + +#ifndef NSSCKBI_H +#define NSSCKBI_H + +/* + * NSS BUILTINS Version numbers. + * + * These are the version numbers for the builtins module packaged with + * this release on NSS. To determine the version numbers of the builtin + * module you are using, use the appropriate PKCS #11 calls. + * + * These version numbers detail changes to the PKCS #11 interface. They map + * to the PKCS #11 spec versions. + */ +#define NSS_BUILTINS_CRYPTOKI_VERSION_MAJOR 2 +#define NSS_BUILTINS_CRYPTOKI_VERSION_MINOR 20 + +/* These version numbers detail the changes + * to the list of trusted certificates. + * + * The NSS_BUILTINS_LIBRARY_VERSION_MINOR macro needs to be bumped + * for each NSS minor release AND whenever we change the list of + * trusted certificates. 10 minor versions are allocated for each + * NSS 3.x branch as follows, allowing us to change the list of + * trusted certificates up to 9 times on each branch. + * - NSS 3.5 branch: 3-9 + * - NSS 3.6 branch: 10-19 + * - NSS 3.7 branch: 20-29 + * - NSS 3.8 branch: 30-39 + * - NSS 3.9 branch: 40-49 + * - NSS 3.10 branch: 50-59 + * - NSS 3.11 branch: 60-69 + * ... + * - NSS 3.14 branch: 90-99 + * ... + * - NSS 3.30 branch: 250-255 + * + * NSS_BUILTINS_LIBRARY_VERSION_MINOR is a CK_BYTE. It's not clear + * whether we may use its full range (0-255) or only 0-99 because + * of the comment in the CK_VERSION type definition. + */ +#define NSS_BUILTINS_LIBRARY_VERSION_MAJOR 1 +#define NSS_BUILTINS_LIBRARY_VERSION_MINOR 62 +#define NSS_BUILTINS_LIBRARY_VERSION "1.62" + +/* These version numbers detail the semantic changes to the ckfw engine. */ +#define NSS_BUILTINS_HARDWARE_VERSION_MAJOR 1 +#define NSS_BUILTINS_HARDWARE_VERSION_MINOR 0 + +/* These version numbers detail the semantic changes to ckbi itself + * (new PKCS #11 objects), etc. */ +#define NSS_BUILTINS_FIRMWARE_VERSION_MAJOR 1 +#define NSS_BUILTINS_FIRMWARE_VERSION_MINOR 0 + +#endif /* NSSCKBI_H */ diff --git a/nsspem.h b/nsspem.h new file mode 100644 index 0000000..1547bf4 --- /dev/null +++ b/nsspem.h @@ -0,0 +1,75 @@ +/* ***** 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): + * Rob Crittenden (rcritten@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 ***** */ + +#ifndef NSSPEM_H +#define NSSPEM_H + +/* + * NSS CKPEM Version numbers. + * + * These are the version numbers for the capi module packaged with + * this release on NSS. To determine the version numbers of the builtin + * module you are using, use the appropriate PKCS #11 calls. + * + * These version numbers detail changes to the PKCS #11 interface. They map + * to the PKCS #11 spec versions. + */ +#define NSS_CKPEM_CRYPTOKI_VERSION_MAJOR 2 +#define NSS_CKPEM_CRYPTOKI_VERSION_MINOR 20 + +/* These version numbers detail the changes + * to the list of trusted certificates. + * + * NSS_CKPEM_LIBRARY_VERSION_MINOR is a CK_BYTE. It's not clear + * whether we may use its full range (0-255) or only 0-99 because + * of the comment in the CK_VERSION type definition. + */ +#define NSS_CKPEM_LIBRARY_VERSION_MAJOR 1 +#define NSS_CKPEM_LIBRARY_VERSION_MINOR 1 +#define NSS_CKPEM_LIBRARY_VERSION "1.1" + +/* These version numbers detail the semantic changes to the ckfw engine. */ +#define NSS_CKPEM_HARDWARE_VERSION_MAJOR 1 +#define NSS_CKPEM_HARDWARE_VERSION_MINOR 0 + +/* These version numbers detail the semantic changes to ckbi itself + * (new PKCS #11 objects), etc. */ +#define NSS_CKPEM_FIRMWARE_VERSION_MAJOR 1 +#define NSS_CKPEM_FIRMWARE_VERSION_MINOR 0 + +#endif /* NSSCKBI_H */ diff --git a/pargs.c b/pargs.c new file mode 100644 index 0000000..f292a8b --- /dev/null +++ b/pargs.c @@ -0,0 +1,162 @@ +/* ***** 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. + * + * Contributor(s): + * Rob Crittenden (rcritten@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 +#include + +void *pem_Malloc(const PRInt32 sz) +{ + return PR_Malloc(sz); +} + +char *pem_StrNdup(const char *instr, PRInt32 inlen) +{ + if (!instr) { + return NULL; + } + + size_t len = inlen; + if (!len) { + return NULL; + } + char *buffer = (char *) pem_Malloc(len + 1); + if (!buffer) { + return NULL; + } + memcpy(buffer, instr, len); + buffer[len] = 0; /* NULL termination */ + return buffer; +} + +char *pem_Strdup(const char *instr) +{ + if (!instr) { + return NULL; + } + + size_t len = strlen(instr); + return pem_StrNdup(instr, len); +} + +void pem_Free(char *instr) +{ + if (!instr) { + PR_ASSERT(0); + } + PR_Free(instr); +} + +void +addString(char ***returnedstrings, char *newstring, PRInt32 stringcount) +{ + char **stringarray = NULL; + if (!returnedstrings || !newstring) { + return; + } + if (!stringcount) { + /* first string to be added, allocate buffer */ + *returnedstrings = + (char **) PR_Malloc(sizeof(char *) * (stringcount + 1)); + stringarray = *returnedstrings; + } else { + stringarray = (char **) PR_Realloc(*returnedstrings, + sizeof(char *) * (stringcount + 1)); + if (stringarray) { + *returnedstrings = stringarray; + } + } + if (stringarray) { + stringarray[stringcount] = newstring; + } +} + +PRBool +pem_ParseString(const char *inputstring, const char delimiter, + PRInt32 * numStrings, char ***returnedstrings) +{ + if (!inputstring || !delimiter || !numStrings || !returnedstrings) { + /* we need a string and a non-zero delimiter, as well as + * a valid place to return the strings and count + */ + return PR_FALSE; + } + char nextchar; + char *instring = (char *) inputstring; + *numStrings = 0; + *returnedstrings = NULL; + + while ((nextchar = *instring)) { + unsigned long len = 0; + char *next = (char *) strchr(instring, delimiter); + if (next) { + /* current string string */ + len = next - instring; + } else { + /* last string length */ + len = strlen(instring); + } + + if (len > 0) { + char *newstring = pem_StrNdup(instring, len); + + addString(returnedstrings, newstring, (*numStrings)++); + + instring += len; + } + + if (delimiter == *instring) { + instring++; /* skip past next delimiter */ + } + } + return PR_TRUE; +} + +PRBool pem_FreeParsedStrings(PRInt32 numStrings, char **instrings) +{ + if (!numStrings || !instrings) { + return PR_FALSE; + } + PRInt32 counter; + for (counter = 0; counter < numStrings; counter++) { + char *astring = instrings[counter]; + if (astring) { + pem_Free(astring); + } + } + PR_Free((void *) instrings); + return PR_TRUE; +} diff --git a/pfind.c b/pfind.c new file mode 100644 index 0000000..0c6a9a7 --- /dev/null +++ b/pfind.c @@ -0,0 +1,588 @@ +/* ***** 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. + * + * Contributor(s): + * Rob Crittenden (rcritten@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 "ckpem.h" + +/* + * pfind.c + * + * This file implements the NSSCKMDFindObjects object for the + * "PEM objects" cryptoki module. + */ + +NSS_EXTERN_DATA pemInternalObject **gobj; +NSS_EXTERN_DATA int pem_nobjs; + +struct pemFOStr { + NSSArena *arena; + CK_ULONG n; + CK_ULONG i; + pemInternalObject **objs; +}; + +#define PEM_ITEM_CHUNK 512 + +#define PUT_Object(obj,err) \ + { \ + if (count >= size) { \ + *listp = *listp ? \ + nss_ZREALLOCARRAY(*listp, pemInternalObject *, \ + (size+PEM_ITEM_CHUNK) ) : \ + nss_ZNEWARRAY(NULL, pemInternalObject *, \ + (size+PEM_ITEM_CHUNK) ) ; \ + if ((pemInternalObject **)NULL == *listp) { \ + err = CKR_HOST_MEMORY; \ + goto loser; \ + } \ + size += PEM_ITEM_CHUNK; \ + } \ + (*listp)[ count ] = (obj); \ + count++; \ + } + +static void +pem_mdFindObjects_Final +( + NSSCKMDFindObjects * mdFindObjects, + NSSCKFWFindObjects * fwFindObjects, + NSSCKMDSession * mdSession, + NSSCKFWSession * fwSession, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance +) +{ + struct pemFOStr *fo = (struct pemFOStr *) mdFindObjects->etc; + NSSArena *arena = fo->arena; + + nss_ZFreeIf(fo->objs); + nss_ZFreeIf(fo); + nss_ZFreeIf(mdFindObjects); + if ((NSSArena *) NULL != arena) { + NSSArena_Destroy(arena); + } + + return; +} + +static NSSCKMDObject * +pem_mdFindObjects_Next +( + NSSCKMDFindObjects * mdFindObjects, + NSSCKFWFindObjects * fwFindObjects, + NSSCKMDSession * mdSession, + NSSCKFWSession * fwSession, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + NSSArena * arena, + CK_RV * pError +) +{ + struct pemFOStr *fo = (struct pemFOStr *) mdFindObjects->etc; + pemInternalObject *io; + + plog("pem_FindObjects_Next: "); + + if (fo->i == fo->n) { + plog("Done creating objects\n"); + *pError = CKR_OK; + return (NSSCKMDObject *) NULL; + } + + io = fo->objs[fo->i]; + fo->i++; + + plog("Creating object for type %d\n", io->type); + + return pem_CreateMDObject(arena, io, pError); +} + +#if 0 +static int +pem_derUnwrapInt(unsigned char *src, int size, unsigned char **dest) +{ + unsigned char *start = src; + int len = 0; + + if (*src++ != 2) { + return 0; + } + len = *src++; + if (len & 0x80) { + int count = len & 0x7f; + len = 0; + + if (count + 2 > size) { + return 0; + } + while (count-- > 0) { + len = (len << 8) | *src++; + } + } + if (len + (src - start) != size) { + return 0; + } + *dest = src; + return len; +} +#endif + +static char * pem_attr_name(CK_ATTRIBUTE_TYPE type) { + switch(type) { + case CKA_CLASS: + return "CKA_CLASS"; + case CKA_TOKEN: + return "CKA_TOKEN"; + case CKA_PRIVATE: + return "CKA_PRIVATE"; + case CKA_LABEL: + return "CKA_LABEL"; + case CKA_APPLICATION: + return "CKA_APPLICATION"; + case CKA_VALUE: + return "CKA_VALUE"; + case CKA_OBJECT_ID: + return "CKA_OBJECT_ID"; + case CKA_CERTIFICATE_TYPE: + return "CKA_CERTIFICATE_TYPE"; + case CKA_ISSUER: + return "CKA_ISSUER"; + case CKA_SERIAL_NUMBER: + return "CKA_SERIAL_NUMBER"; + case CKA_ID: + return "CKA_ID"; + default: + return "unknown"; + } +} + +static CK_BBOOL +pem_attrmatch(CK_ATTRIBUTE_PTR a, pemInternalObject * o) { + PRBool prb; + const NSSItem *b; + + b = pem_FetchAttribute(o, a->type); + if (b == NULL) { + plog("pem_attrmatch %s %08x: CK_FALSE attr not found\n", pem_attr_name(a->type), a->type); + return CK_FALSE; + } + + if (a->ulValueLen != b->size) { + plog("pem_attrmatch %s %08x: CK_FALSE size mismatch %d vs %d\n", pem_attr_name(a->type), a->type, a->ulValueLen, b->size); + return CK_FALSE; + } + + prb = nsslibc_memequal(a->pValue, b->data, b->size, (PRStatus *) NULL); + + if (PR_TRUE == prb) { + plog("pem_attrmatch %s %08x: CK_TRUE\n", pem_attr_name(a->type), a->type); + return CK_TRUE; + } else { + plog("pem_attrmatch %s %08x: CK_FALSE\n", pem_attr_name(a->type), a->type); + plog("type: %08x, label: %s a->pValue %08x, b->data %08x\n", o->objClass, o->u.cert.label.data, a->pValue, b->data); + return CK_FALSE; + } +} + +static CK_BBOOL +pem_match +( + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + pemInternalObject * o +) +{ + CK_ULONG i; + + for (i = 0; i < ulAttributeCount; i++) { + if (CK_FALSE == pem_attrmatch(&pTemplate[i], o)) { + plog("pem_match: CK_FALSE\n"); + return CK_FALSE; + } + } + + /* Every attribute passed */ + plog("pem_match: CK_TRUE\n"); + return CK_TRUE; +} + +CK_OBJECT_CLASS +pem_GetObjectClass(CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount) +{ + CK_ULONG i; + + for (i = 0; i < ulAttributeCount; i++) { + if (pTemplate[i].type == CKA_CLASS) { + return *(CK_OBJECT_CLASS *) pTemplate[i].pValue; + } + } + /* need to return a value that says 'fetch them all' */ + return CK_INVALID_HANDLE; +} + +static PRUint32 +collect_objects(CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + pemInternalObject *** listp, + CK_RV * pError, CK_SLOT_ID slotID) +{ + PRUint32 i; + PRUint32 count = 0; + PRUint32 size = 0; + CK_OBJECT_CLASS objClass; + + plog("collect_objects slot #%ld, ", slotID); + plog("%d attributes, ", ulAttributeCount); + plog("%d objects to look through.\n", pem_nobjs); + plog("Looking for: "); + /* + * now handle the various object types + */ + objClass = pem_GetObjectClass(pTemplate, ulAttributeCount); + *pError = CKR_OK; + switch (objClass) { + case CKO_CERTIFICATE: + plog("CKO_CERTIFICATE\n"); + for (i = 0; i < pem_nobjs; i++) { + plog(" %d type = %d\n", i, gobj[i]->type); + if (gobj[i]->type != pemCert) + continue; + if ((slotID == gobj[i]->slotID) + && (CK_TRUE == + pem_match(pTemplate, ulAttributeCount, gobj[i]))) { + + pemInternalObject *o = NULL; + + o = nss_ZNEW(NULL, pemInternalObject); + if ((pemInternalObject *) NULL == o) { + *pError = CKR_HOST_MEMORY; + goto loser; + } + memset(&o->u.cert, 0, sizeof(o->u.cert)); + o->objClass = objClass; + o->type = pemCert; + o->derCert = nss_ZNEW(NULL, SECItem); + o->derCert->data = + (void *) nss_ZAlloc(NULL, gobj[i]->derCert->len); + o->derCert->len = gobj[i]->derCert->len; + nsslibc_memcpy(o->derCert->data, gobj[i]->derCert->data, + gobj[i]->derCert->len); + + o->u.cert.subject.data = + (void *) nss_ZAlloc(NULL, + gobj[i]->u.cert.subject.size); + o->u.cert.subject.size = gobj[i]->u.cert.subject.size; + nsslibc_memcpy(o->u.cert.subject.data, + gobj[i]->u.cert.subject.data, + gobj[i]->u.cert.subject.size); + + o->u.cert.issuer.data = + (void *) nss_ZAlloc(NULL, + gobj[i]->u.cert.issuer.size); + o->u.cert.issuer.size = gobj[i]->u.cert.issuer.size; + nsslibc_memcpy(o->u.cert.issuer.data, + gobj[i]->u.cert.issuer.data, + gobj[i]->u.cert.issuer.size); + + o->u.cert.serial.data = + (void *) nss_ZAlloc(NULL, + gobj[i]->u.cert.serial.size); + o->u.cert.serial.size = gobj[i]->u.cert.serial.size; + nsslibc_memcpy(o->u.cert.serial.data, + gobj[i]->u.cert.serial.data, + gobj[i]->u.cert.serial.size); + + o->nickname = + (char *) nss_ZAlloc(NULL, + strlen(gobj[i]->nickname) + 1); + strcpy(o->nickname, gobj[i]->nickname); + o->id.data = + (void *) nss_ZAlloc(NULL, gobj[i]->id.size); + (void) nsslibc_memcpy(o->id.data, gobj[i]->id.data, + gobj[i]->id.size); + o->id.size = gobj[i]->id.size; + PUT_Object(o, *pError); + } /* match */ + } /* for */ + break; + case CKO_PUBLIC_KEY: +#if 0 + for (i = 0; i < pem_nobjs; i++) { + if (gobj[i]->type != pemBareKey) + continue; + if (CK_TRUE == pem_match(pTemplate, ulAttributeCount, gobj[i])) { + pemInternalObject *o; + + o = nss_ZNEW(NULL, pemInternalObject); + if ((pemInternalObject *) NULL == o) { + *pError = CKR_HOST_MEMORY; + goto loser; + } + memset(&o->u.key, 0, sizeof(o->u.key)); + o->objClass = objClass; + o->type = pemBareKey; + o->derCert = nss_ZNEW(NULL, SECItem); + o->derCert->data = + (void *) nss_ZAlloc(NULL, gobj[i]->derCert->len); + o->derCert->len = gobj[i]->derCert->len; + o->id.data = + (void *) nss_ZAlloc(NULL, + sizeof(gobj[i]->id.data)); + o->id.data = gobj[i]->id.data; + o->id.size = gobj[i]->id.size; + nsslibc_memcpy(o->derCert->data, gobj[i]->derCert->data, + gobj[i]->derCert->len); + o->nickname = + (char *) nss_ZAlloc(NULL, + strlen(gobj[i]->nickname) + 1); + strcpy(o->nickname, gobj[i]->nickname); + o->u.key.key.privateKey = nss_ZNEW(NULL, SECItem); + o->u.key.key.privateKey->data = + (void *) nss_ZAlloc(NULL, + gobj[i]->u.key.key.privateKey-> + len); + o->u.key.key.privateKey->len = + gobj[i]->u.key.key.privateKey->len; + nsslibc_memcpy(o->u.key.key.privateKey->data, + gobj[i]->u.key.key.privateKey->data, + gobj[i]->u.key.key.privateKey->len); + + PUT_Object(o, *pError); + } // match + } // for + goto done; + break; +#endif + case CKO_PRIVATE_KEY: + plog("CKO_PRIVATE_KEY\n"); + for (i = 0; i < pem_nobjs; i++) { + if (gobj[i]->type != pemBareKey) + continue; + if ((slotID == gobj[i]->slotID) + && (CK_TRUE == + pem_match(pTemplate, ulAttributeCount, gobj[i]))) { + pemInternalObject *o; + + o = nss_ZNEW(NULL, pemInternalObject); + if ((pemInternalObject *) NULL == o) { + *pError = CKR_HOST_MEMORY; + goto loser; + } + memset(&o->u.key, 0, sizeof(o->u.key)); + o->objClass = objClass; + o->type = pemBareKey; + o->derCert = nss_ZNEW(NULL, SECItem); + o->derCert->data = + (void *) nss_ZAlloc(NULL, gobj[i]->derCert->len); + o->derCert->len = gobj[i]->derCert->len; + o->id.data = + (void *) nss_ZAlloc(NULL, gobj[i]->id.size); + (void) nsslibc_memcpy(o->id.data, gobj[i]->id.data, + gobj[i]->id.size); + o->id.size = gobj[i]->id.size; + nsslibc_memcpy(o->derCert->data, gobj[i]->derCert->data, + gobj[i]->derCert->len); + o->nickname = + (char *) nss_ZAlloc(NULL, + strlen(gobj[i]->nickname) + 1); + strcpy(o->nickname, gobj[i]->nickname); + o->u.key.key.privateKey = nss_ZNEW(NULL, SECItem); + o->u.key.key.privateKey->data = + (void *) nss_ZAlloc(NULL, + gobj[i]->u.key.key.privateKey-> + len); + o->u.key.key.privateKey->len = + gobj[i]->u.key.key.privateKey->len; + nsslibc_memcpy(o->u.key.key.privateKey->data, + gobj[i]->u.key.key.privateKey->data, + gobj[i]->u.key.key.privateKey->len); + + PUT_Object(o, *pError); + } /* match */ + } /* for */ + goto done; + break; + case CKO_NETSCAPE_TRUST: + plog("CKO_NETSCAPE_TRUST\n"); + for (i = 0; i < pem_nobjs; i++) { + if (gobj[i]->type != pemTrust) + continue; + if ((slotID == gobj[i]->slotID) + && (CK_TRUE == + pem_match(pTemplate, ulAttributeCount, gobj[i]))) { + pemInternalObject *o; + + o = nss_ZNEW(NULL, pemInternalObject); + if ((pemInternalObject *) NULL == o) { + *pError = CKR_HOST_MEMORY; + goto loser; + } + memset(&o->u.trust, 0, sizeof(o->u.trust)); + o->objClass = objClass; + o->type = pemTrust; + o->derCert = nss_ZNEW(NULL, SECItem); + o->derCert->data = + (void *) nss_ZAlloc(NULL, gobj[i]->derCert->len); + o->derCert->len = gobj[i]->derCert->len; + nsslibc_memcpy(o->derCert->data, gobj[i]->derCert->data, + gobj[i]->derCert->len); + o->nickname = + (char *) nss_ZAlloc(NULL, + strlen(gobj[i]->nickname) + 1); + strcpy(o->nickname, gobj[i]->nickname); + o->id.data = + (void *) nss_ZAlloc(NULL, gobj[i]->id.size); + (void) nsslibc_memcpy(o->id.data, gobj[i]->id.data, + gobj[i]->id.size); + o->id.size = gobj[i]->id.size; + + PUT_Object(o, *pError); + } /* match */ + } /* for */ + goto done; + case CKO_NETSCAPE_CRL: + plog("CKO_NETSCAPE_CRL\n"); + break; + case CKO_NETSCAPE_SMIME: + plog("CKO_NETSCAPE_SMIME\n"); + break; + case CKO_NETSCAPE_BUILTIN_ROOT_LIST: + plog("CKO_NETSCAPE_BUILTIN_ROOT_LIST\n"); + break; + case CK_INVALID_HANDLE: + plog("CK_INVALID_HANDLE\n"); + break; + default: + plog("no other object types %08x\n", objClass); + goto done; /* no other object types we understand in this module */ + } + + if (CKR_OK != *pError) { + goto loser; + } + + done: + plog("collect_objects: Found %d\n", count); + return count; + loser: + nss_ZFreeIf(*listp); + return 0; + +} + +NSS_IMPLEMENT NSSCKMDFindObjects * +pem_FindObjectsInit +( + NSSCKFWSession * fwSession, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_RV * pError +) +{ + NSSArena *arena; + NSSCKMDFindObjects *rv = (NSSCKMDFindObjects *) NULL; + struct pemFOStr *fo = (struct pemFOStr *) NULL; + pemInternalObject **temp = (pemInternalObject **) NULL; + NSSCKFWSlot *fwSlot; + CK_SLOT_ID slotID; + + plog("pem_FindObjectsInit\n"); + fwSlot = nssCKFWSession_GetFWSlot(fwSession); + if ((NSSCKFWSlot *) NULL == fwSlot) { + goto loser; + } + slotID = nssCKFWSlot_GetSlotID(fwSlot); + + arena = NSSArena_Create(); + if ((NSSArena *) NULL == arena) { + goto loser; + } + + rv = nss_ZNEW(arena, NSSCKMDFindObjects); + if ((NSSCKMDFindObjects *) NULL == rv) { + *pError = CKR_HOST_MEMORY; + goto loser; + } + + fo = nss_ZNEW(arena, struct pemFOStr); + if ((struct pemFOStr *) NULL == fo) { + *pError = CKR_HOST_MEMORY; + goto loser; + } + + fo->arena = arena; + /* fo->n and fo->i are already zero */ + + rv->etc = (void *) fo; + rv->Final = pem_mdFindObjects_Final; + rv->Next = pem_mdFindObjects_Next; + rv->null = (void *) NULL; + + fo->n = + collect_objects(pTemplate, ulAttributeCount, &temp, pError, + slotID); + if (*pError != CKR_OK) { + goto loser; + } + + fo->objs = nss_ZNEWARRAY(arena, pemInternalObject *, fo->n); + if ((pemInternalObject **) NULL == fo->objs) { + *pError = CKR_HOST_MEMORY; + goto loser; + } + + (void) nsslibc_memcpy(fo->objs, temp, + sizeof(pemInternalObject *) * fo->n); + + nss_ZFreeIf(temp); + temp = (pemInternalObject **) NULL; + + return rv; + + loser: + nss_ZFreeIf(temp); + nss_ZFreeIf(fo); + nss_ZFreeIf(rv); + if ((NSSArena *) NULL != arena) { + NSSArena_Destroy(arena); + } + return (NSSCKMDFindObjects *) NULL; +} diff --git a/pinst.c b/pinst.c new file mode 100644 index 0000000..17ffff0 --- /dev/null +++ b/pinst.c @@ -0,0 +1,602 @@ +/* ***** 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. + * + * Contributor(s): + * Rob Crittenden (rcritten@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 +#include "ckpem.h" +#include "blapi.h" + +/* + * pinstance.c + * + * This file implements the NSSCKMDInstance object for the + * "PEM objects" cryptoki module. + */ + +static PRBool pemInitialized = PR_FALSE; + +pemInternalObject **gobj; +int pem_nobjs = 0; +int token_needsLogin[NUM_SLOTS]; + +PRInt32 size = 0; +PRInt32 count = 0; + +#define PEM_ITEM_CHUNK 512 + +#define PUT_Object(obj,err) \ + { \ + if (count >= size) { \ + gobj = gobj ? \ + nss_ZREALLOCARRAY(gobj, pemInternalObject *, \ + (size+PEM_ITEM_CHUNK) ) : \ + nss_ZNEWARRAY(NULL, pemInternalObject *, \ + (size+PEM_ITEM_CHUNK) ) ; \ + if ((pemInternalObject **)NULL == gobj) { \ + err = CKR_HOST_MEMORY; \ + goto loser; \ + } \ + size += PEM_ITEM_CHUNK; \ + } \ + (gobj)[ count ] = (obj); \ + count++; \ + } + +/* + * simple cert decoder to avoid the cost of asn1 engine + */ +static unsigned char * +dataStart(unsigned char *buf, unsigned int length, + unsigned int *data_length, + PRBool includeTag, unsigned char *rettag) +{ + unsigned char tag; + unsigned int used_length = 0; + + tag = buf[used_length++]; + + if (rettag) { + *rettag = tag; + } + + /* blow out when we come to the end */ + if (tag == 0) { + return NULL; + } + + *data_length = buf[used_length++]; + + if (*data_length & 0x80) { + int len_count = *data_length & 0x7f; + + *data_length = 0; + + while (len_count-- > 0) { + *data_length = (*data_length << 8) | buf[used_length++]; + } + } + + if (*data_length > (length - used_length)) { + *data_length = length - used_length; + return NULL; + } + if (includeTag) + *data_length += used_length; + + return (buf + (includeTag ? 0 : used_length)); +} + +static int +GetCertFields(unsigned char *cert, int cert_length, + SECItem * issuer, SECItem * serial, SECItem * derSN, + SECItem * subject, SECItem * valid, SECItem * subjkey) +{ + unsigned char *buf; + unsigned int buf_length; + unsigned char *dummy; + unsigned int dummylen; + + /* get past the signature wrap */ + buf = dataStart(cert, cert_length, &buf_length, PR_FALSE, NULL); + if (buf == NULL) + return SECFailure; + /* get into the raw cert data */ + buf = dataStart(buf, buf_length, &buf_length, PR_FALSE, NULL); + if (buf == NULL) + return SECFailure; + /* skip past any optional version number */ + if ((buf[0] & 0xa0) == 0xa0) { + dummy = dataStart(buf, buf_length, &dummylen, PR_FALSE, NULL); + if (dummy == NULL) + return SECFailure; + buf_length -= (dummy - buf) + dummylen; + buf = dummy + dummylen; + } + /* serial number */ + if (derSN) { + derSN->data = + dataStart(buf, buf_length, &derSN->len, PR_TRUE, NULL); + } + serial->data = + dataStart(buf, buf_length, &serial->len, PR_FALSE, NULL); + if (serial->data == NULL) + return SECFailure; + buf_length -= (serial->data - buf) + serial->len; + buf = serial->data + serial->len; + /* skip the OID */ + dummy = dataStart(buf, buf_length, &dummylen, PR_FALSE, NULL); + if (dummy == NULL) + return SECFailure; + buf_length -= (dummy - buf) + dummylen; + buf = dummy + dummylen; + /* issuer */ + issuer->data = dataStart(buf, buf_length, &issuer->len, PR_TRUE, NULL); + if (issuer->data == NULL) + return SECFailure; + buf_length -= (issuer->data - buf) + issuer->len; + buf = issuer->data + issuer->len; + + /* only wanted issuer/SN */ + if (valid == NULL) { + return SECSuccess; + } + /* validity */ + valid->data = dataStart(buf, buf_length, &valid->len, PR_FALSE, NULL); + if (valid->data == NULL) + return SECFailure; + buf_length -= (valid->data - buf) + valid->len; + buf = valid->data + valid->len; + /*subject */ + subject->data = + dataStart(buf, buf_length, &subject->len, PR_TRUE, NULL); + if (subject->data == NULL) + return SECFailure; + buf_length -= (subject->data - buf) + subject->len; + buf = subject->data + subject->len; + /* subject key info */ + subjkey->data = + dataStart(buf, buf_length, &subjkey->len, PR_TRUE, NULL); + if (subjkey->data == NULL) + return SECFailure; + buf_length -= (subjkey->data - buf) + subjkey->len; + buf = subjkey->data + subjkey->len; + return SECSuccess; +} + +pemInternalObject * +CreateObject(CK_OBJECT_CLASS objClass, + pemObjectType type, SECItem * certDER, + SECItem * keyDER, char *filename, + int objid, CK_SLOT_ID slotID) +{ + pemInternalObject *o; + SECItem subject; + SECItem issuer; + SECItem serial; + SECItem derSN; + SECItem valid; + SECItem subjkey; + char id[16]; + char *nickname; + int len; + + o = nss_ZNEW(NULL, pemInternalObject); + if ((pemInternalObject *) NULL == o) { + return NULL; + } + + nickname = strrchr(filename, '/'); + if (nickname) + nickname++; + else + nickname = filename; + + switch (objClass) { + case CKO_CERTIFICATE: + plog("Creating cert nick %s id %d in slot %ld\n", nickname, objid, slotID); + memset(&o->u.cert, 0, sizeof(o->u.cert)); + break; + case CKO_PRIVATE_KEY: + plog("Creating key id %d in slot %ld\n", objid, slotID); + memset(&o->u.key, 0, sizeof(o->u.key)); + break; + case CKO_NETSCAPE_TRUST: + plog("Creating trust nick %s id %d in slot %ld\n", nickname, objid, slotID); + memset(&o->u.trust, 0, sizeof(o->u.trust)); + break; + } + o->objClass = objClass; + o->type = type; + o->slotID = slotID; + o->derCert = nss_ZNEW(NULL, SECItem); + o->derCert->data = (void *) nss_ZAlloc(NULL, certDER->len); + o->derCert->len = certDER->len; + nsslibc_memcpy(o->derCert->data, certDER->data, certDER->len); + + switch (objClass) { + case CKO_CERTIFICATE: + case CKO_NETSCAPE_TRUST: + GetCertFields(o->derCert->data, + o->derCert->len, &issuer, &serial, + &derSN, &subject, &valid, &subjkey); + + o->u.cert.subject.data = (void *) nss_ZAlloc(NULL, subject.len); + o->u.cert.subject.size = subject.len; + nsslibc_memcpy(o->u.cert.subject.data, subject.data, subject.len); + + o->u.cert.issuer.data = (void *) nss_ZAlloc(NULL, issuer.len); + o->u.cert.issuer.size = issuer.len; + nsslibc_memcpy(o->u.cert.issuer.data, issuer.data, issuer.len); + + o->u.cert.serial.data = (void *) nss_ZAlloc(NULL, serial.len); + o->u.cert.serial.size = serial.len; + nsslibc_memcpy(o->u.cert.serial.data, serial.data, serial.len); + break; + case CKO_PRIVATE_KEY: + o->u.key.key.privateKey = nss_ZNEW(NULL, SECItem); + o->u.key.key.privateKey->data = + (void *) nss_ZAlloc(NULL, keyDER->len); + o->u.key.key.privateKey->len = keyDER->len; + nsslibc_memcpy(o->u.key.key.privateKey->data, keyDER->data, + keyDER->len); + } + + o->nickname = (char *) nss_ZAlloc(NULL, strlen(nickname) + 1); + strcpy(o->nickname, nickname); + + sprintf(id, "%d", objid); + + len = strlen(id) + 1; /* zero terminate */ + o->id.data = (void *) nss_ZAlloc(NULL, len); + (void) nsslibc_memcpy(o->id.data, id, len); + o->id.size = len; + + return o; +} + +CK_RV +AddCertificate(char *certfile, char *keyfile, PRBool cacert, + CK_SLOT_ID slotID) +{ + pemInternalObject *o; + SECItem certDER; + CK_RV error = 0; + int objid, i; + int nobjs = 0; + SECItem **objs = NULL; + char *ivstring = NULL; + int cipher; + + certDER.data = NULL; + nobjs = ReadDERFromFile(&objs, certfile, PR_TRUE, &cipher, &ivstring, PR_TRUE /* certs only */); + if (nobjs <= 0) { + nss_ZFreeIf(objs); + return CKR_GENERAL_ERROR; + } + + /* For now load as many certs as are in the file for CAs only */ + if (cacert) { + for (i = 0; i < nobjs; i++) { + char nickname[1024]; + objid = pem_nobjs + 1; + + snprintf(nickname, 1024, "%s - %d", certfile, i); + + o = CreateObject(CKO_CERTIFICATE, pemCert, objs[i], NULL, + nickname, 0, slotID); + if (o == NULL) { + error = CKR_GENERAL_ERROR; + goto loser; + } + PUT_Object(o, error); + if (error != CKR_OK) + goto loser; + o = NULL; + pem_nobjs++; + + /* Add the CA trust object */ + o = CreateObject(CKO_NETSCAPE_TRUST, pemTrust, objs[i], NULL, + nickname, 0, slotID); + if (o == NULL) { + error = CKR_GENERAL_ERROR; + goto loser; + } + PUT_Object(o, error); + pem_nobjs++; + } /* for */ + } else { + objid = pem_nobjs + 1; + o = CreateObject(CKO_CERTIFICATE, pemCert, objs[0], NULL, certfile, + objid, slotID); + if (o == NULL) { + error = CKR_GENERAL_ERROR; + goto loser; + } + + PUT_Object(o, error); + + if (error != CKR_OK) + goto loser; + + o = NULL; + pem_nobjs++; + + if (keyfile) { /* add the private key */ + SECItem **keyobjs = NULL; + int kobjs = 0; + kobjs = + ReadDERFromFile(&keyobjs, keyfile, PR_TRUE, &cipher, + &ivstring, PR_FALSE); + if (kobjs < 1) { + error = CKR_GENERAL_ERROR; + goto loser; + } + o = CreateObject(CKO_PRIVATE_KEY, pemBareKey, objs[0], + keyobjs[0], certfile, objid, slotID); + if (o == NULL) { + error = CKR_GENERAL_ERROR; + goto loser; + } + + PUT_Object(o, error); + pem_nobjs++; + } + } + + nss_ZFreeIf(objs); + return CKR_OK; + + loser: + nss_ZFreeIf(objs); + nss_ZFreeIf(o); + return error; +} + +CK_RV +pem_Initialize +( + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + NSSUTF8 * configurationData +) +{ + CK_RV rv; + /* parse the initialization string and initialize CRLInstances */ + char **certstrings = NULL; + PRInt32 numcerts = 0; + PRBool status, error = PR_FALSE; + int i; + + if (pemInitialized) { + return CKR_OK; + } + RNG_RNGInit(); + + open_log(); + + plog("pem_Initialize\n"); + + unsigned char *modparms = NULL; + if (!fwInstance) { + return CKR_ARGUMENTS_BAD; + } + + CK_C_INITIALIZE_ARGS_PTR modArgs = + NSSCKFWInstance_GetInitArgs(fwInstance); + if (!modArgs) { + goto done; + } + if (modArgs->LibraryParameters) { + modparms = (unsigned char *) modArgs->LibraryParameters; + } + if (modparms) + plog("Initialized with %s\n", modparms); + + /* + * The initialization string format is a space-delimited file of + * pairs of paths which are delimited by a semi-colon. The first + * entry of the pair is the path to the certificate file. The + * second is the path to the key file. + * + * CA certificates do not need the semi-colon. + * + * Example: + * /etc/certs/server.pem;/etc/certs/server.key /etc/certs/ca.pem + * + */ + status = + pem_ParseString((const char *) modparms, ' ', &numcerts, + &certstrings); + if (status == PR_FALSE) { + return CKR_ARGUMENTS_BAD; + } + + for (i = 0; i < numcerts && error != PR_TRUE; i++) { + char *cert = certstrings[i]; + PRInt32 attrcount = 0; + char **certattrs = NULL; + status = pem_ParseString(cert, ';', &attrcount, &certattrs); + if (status == PR_FALSE) { + error = PR_TRUE; + break; + } + + if (error == PR_FALSE) { + if (attrcount == 1) /* CA certificate */ + rv = AddCertificate(certattrs[0], NULL, PR_TRUE, 0); + else + rv = AddCertificate(certattrs[0], certattrs[1], PR_FALSE, + 0); + + if (rv != CKR_OK) { + error = PR_TRUE; + status = PR_FALSE; + } + } + pem_FreeParsedStrings(attrcount, certattrs); + } + pem_FreeParsedStrings(numcerts, certstrings); + + if (status == PR_FALSE) { + return CKR_ARGUMENTS_BAD; + } + + for (i = 0; i < NUM_SLOTS; i++) + token_needsLogin[i] = PR_FALSE; + + done: + + PR_AtomicSet(&pemInitialized, PR_TRUE); + + return CKR_OK; +} + +void +pem_Finalize +( + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance +) +{ + plog("pem_Finalize\n"); + if (!pemInitialized) + return; + + PR_AtomicSet(&pemInitialized, PR_FALSE); + + return; +} + +/* + * NSSCKMDInstance methods + */ + +static CK_ULONG +pem_mdInstance_GetNSlots +( + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_RV * pError +) +{ + return (CK_ULONG) NUM_SLOTS; +} + +static CK_VERSION +pem_mdInstance_GetCryptokiVersion +( + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance +) +{ + return pem_CryptokiVersion; +} + +static NSSUTF8 * +pem_mdInstance_GetManufacturerID +( + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_RV * pError +) +{ + return (NSSUTF8 *) pem_ManufacturerID; +} + +static NSSUTF8 * +pem_mdInstance_GetLibraryDescription +( + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_RV * pError +) +{ + return (NSSUTF8 *) pem_LibraryDescription; +} + +static CK_VERSION +pem_mdInstance_GetLibraryVersion +( + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance +) +{ + return pem_LibraryVersion; +} + +static CK_RV +pem_mdInstance_GetSlots +( + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + NSSCKMDSlot * slots[] +) +{ + int i; + CK_RV pError; + + for (i = 0; i < NUM_SLOTS; i++) { + slots[i] = (NSSCKMDSlot *) pem_NewSlot(fwInstance, &pError); + if (pError != CKR_OK) + return pError; + } + return CKR_OK; +} + +CK_BBOOL +pem_mdInstance_ModuleHandlesSessionObjects +( + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance +) +{ + return CK_TRUE; +} + +NSS_IMPLEMENT_DATA const NSSCKMDInstance +pem_mdInstance = { + (void *) NULL, /* etc */ + pem_Initialize, /* Initialize */ + pem_Finalize, /* Finalize */ + pem_mdInstance_GetNSlots, + pem_mdInstance_GetCryptokiVersion, + pem_mdInstance_GetManufacturerID, + pem_mdInstance_GetLibraryDescription, + pem_mdInstance_GetLibraryVersion, + pem_mdInstance_ModuleHandlesSessionObjects, + pem_mdInstance_GetSlots, + NULL, /* WaitForSlotEvent */ + (void *) NULL /* null terminator */ +}; diff --git a/pobject.c b/pobject.c new file mode 100644 index 0000000..2fcc9c8 --- /dev/null +++ b/pobject.c @@ -0,0 +1,1175 @@ +/* ***** 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. + * + * Contributor(s): + * Rob Crittenden (rcritten@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 "ckpem.h" +#include "secasn1.h" +#include "certt.h" +#include "pk11pub.h" + +/* + * pobject.c + * + * This file implements the NSSCKMDObject object for the + * "PEM objects" cryptoki module. + */ + +NSS_EXTERN_DATA pemInternalObject **gobj; +NSS_EXTERN_DATA int pem_nobjs; +NSS_EXTERN_DATA int token_needsLogin[NUM_SLOTS]; + +#define PEM_ITEM_CHUNK 512 +#define PUT_Object(obj,err) \ + { \ + if (count >= size) { \ + gobj = gobj ? \ + nss_ZREALLOCARRAY(gobj, pemInternalObject *, \ + (size+PEM_ITEM_CHUNK) ) : \ + nss_ZNEWARRAY(NULL, pemInternalObject *, \ + (size+PEM_ITEM_CHUNK) ) ; \ + if ((pemInternalObject **)NULL == gobj) { \ + err = CKR_HOST_MEMORY; \ + goto loser; \ + } \ + size += PEM_ITEM_CHUNK; \ + } \ + (gobj)[ count ] = (obj); \ + count++; \ + } + +const CK_ATTRIBUTE_TYPE certAttrs[] = { + CKA_CLASS, + CKA_TOKEN, + CKA_PRIVATE, + CKA_MODIFIABLE, + CKA_LABEL, + CKA_CERTIFICATE_TYPE, + CKA_SUBJECT, + CKA_ISSUER, + CKA_SERIAL_NUMBER, + CKA_VALUE +}; +const PRUint32 certAttrsCount = NSS_PEM_ARRAY_SIZE(certAttrs); + +/* private keys, for now only support RSA */ +const CK_ATTRIBUTE_TYPE privKeyAttrs[] = { + CKA_CLASS, + CKA_TOKEN, + CKA_PRIVATE, + CKA_MODIFIABLE, + CKA_LABEL, + CKA_KEY_TYPE, + CKA_DERIVE, + CKA_LOCAL, + CKA_SUBJECT, + CKA_SENSITIVE, + CKA_DECRYPT, + CKA_SIGN, + CKA_SIGN_RECOVER, + CKA_UNWRAP, + CKA_EXTRACTABLE, + CKA_ALWAYS_SENSITIVE, + CKA_NEVER_EXTRACTABLE, + CKA_MODULUS, + CKA_PUBLIC_EXPONENT, +}; +const PRUint32 privKeyAttrsCount = NSS_PEM_ARRAY_SIZE(privKeyAttrs); + +/* public keys, for now only support RSA */ +const CK_ATTRIBUTE_TYPE pubKeyAttrs[] = { + CKA_CLASS, + CKA_TOKEN, + CKA_PRIVATE, + CKA_MODIFIABLE, + CKA_LABEL, + CKA_KEY_TYPE, + CKA_DERIVE, + CKA_LOCAL, + CKA_SUBJECT, + CKA_ENCRYPT, + CKA_VERIFY, + CKA_VERIFY_RECOVER, + CKA_WRAP, + CKA_MODULUS, + CKA_PUBLIC_EXPONENT, +}; +const PRUint32 pubKeyAttrsCount = NSS_PEM_ARRAY_SIZE(pubKeyAttrs); + +/* Trust */ +const CK_ATTRIBUTE_TYPE trustAttrs[] = { + CKA_CLASS, + CKA_TOKEN, + CKA_LABEL, + CKA_CERT_SHA1_HASH, + CKA_CERT_MD5_HASH, + CKA_ISSUER, + CKA_SUBJECT, + CKA_TRUST_SERVER_AUTH, + CKA_TRUST_CLIENT_AUTH, + CKA_TRUST_EMAIL_PROTECTION, + CKA_TRUST_CODE_SIGNING +}; +const PRUint32 trustAttrsCount = NSS_PEM_ARRAY_SIZE(trustAttrs); + +static const CK_BBOOL ck_true = CK_TRUE; +static const CK_BBOOL ck_false = CK_FALSE; +static const CK_CERTIFICATE_TYPE ckc_x509 = CKC_X_509; +static const CK_KEY_TYPE ckk_rsa = CKK_RSA; +static const CK_OBJECT_CLASS cko_certificate = CKO_CERTIFICATE; +static const CK_OBJECT_CLASS cko_private_key = CKO_PRIVATE_KEY; +static const CK_OBJECT_CLASS cko_public_key = CKO_PUBLIC_KEY; +static const CK_OBJECT_CLASS cko_trust = CKO_NETSCAPE_TRUST; +static const CK_TRUST ckt_netscape_trusted = CKT_NETSCAPE_TRUSTED_DELEGATOR; +static const NSSItem pem_trueItem = { + (void *) &ck_true, (PRUint32) sizeof(CK_BBOOL) +}; +static const NSSItem pem_falseItem = { + (void *) &ck_false, (PRUint32) sizeof(CK_BBOOL) +}; +static const NSSItem pem_x509Item = { + (void *) &ckc_x509, (PRUint32) sizeof(CKC_X_509) +}; +static const NSSItem pem_rsaItem = { + (void *) &ckk_rsa, (PRUint32) sizeof(CK_KEY_TYPE) +}; +static const NSSItem pem_certClassItem = { + (void *) &cko_certificate, (PRUint32) sizeof(CK_OBJECT_CLASS) +}; +static const NSSItem pem_privKeyClassItem = { + (void *) &cko_private_key, (PRUint32) sizeof(CK_OBJECT_CLASS) +}; +static const NSSItem pem_pubKeyClassItem = { + (void *) &cko_public_key, (PRUint32) sizeof(CK_OBJECT_CLASS) +}; +static const NSSItem pem_trustClassItem = { + (void *) &cko_trust, (PRUint32) sizeof(CK_OBJECT_CLASS) +}; +static const NSSItem pem_emptyItem = { + (void *) &ck_true, 0 +}; +static const NSSItem pem_trusted = { + (void *) &ckt_netscape_trusted, (PRUint32) sizeof(CK_TRUST) +}; + +/* + * Template for skipping a subitem. + * + * Note that it only makes sense to use this for decoding (when you want + * to decode something where you are only interested in one or two of + * the fields); you cannot encode a SKIP! + */ +const SEC_ASN1Template SEC_SkipTemplate[] = { + {SEC_ASN1_SKIP} +}; + +/* + * Find the subjectName in a DER encoded certificate + */ +const SEC_ASN1Template SEC_CertSubjectTemplate[] = { + {SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(SECItem)} , + {SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_CONTEXT_SPECIFIC | 0, + 0, SEC_SkipTemplate} , /* version */ + {SEC_ASN1_SKIP}, /* serial number */ + {SEC_ASN1_SKIP}, /* signature algorithm */ + {SEC_ASN1_SKIP}, /* issuer */ + {SEC_ASN1_SKIP}, /* validity */ + {SEC_ASN1_ANY, 0, NULL}, /* subject */ + {SEC_ASN1_SKIP_REST}, + {0} +}; + +void +pem_FetchLabel +( + pemInternalObject * io +) +{ + pemCertObject *co = &io->u.cert; + + co->label.data = io->nickname; + co->label.size = strlen(io->nickname); +} + +const NSSItem +*pem_FetchCertAttribute +( + pemInternalObject * io, + CK_ATTRIBUTE_TYPE type +) +{ + switch (type) { + case CKA_CLASS: + plog(" fetch cert CKA_CLASS\n"); + return &pem_certClassItem; + case CKA_TOKEN: + plog(" fetch cert CKA_TOKEN\n"); + return &pem_trueItem; + case CKA_PRIVATE: + return &pem_falseItem; + case CKA_CERTIFICATE_TYPE: + plog(" fetch cert CKA_CERTIFICATE_TYPE\n"); + return &pem_x509Item; + case CKA_LABEL: + if (0 == io->u.cert.label.size) { + pem_FetchLabel(io); + } + plog(" fetch cert CKA_LABEL %s\n", io->u.cert.label.data); + return &io->u.cert.label; + case CKA_SUBJECT: + plog(" fetch cert CKA_SUBJECT size %d\n", io->u.cert.subject.size); + return &io->u.cert.subject; + case CKA_ISSUER: + plog(" fetch cert CKA_ISSUER size %d\n", io->u.cert.issuer.size); + return &io->u.cert.issuer; + case CKA_SERIAL_NUMBER: + plog(" fetch cert CKA_SERIAL_NUMBER size %d value %08x\n", io->u.cert.serial.size, io->u.cert.serial.data); + return &io->u.cert.serial; + case CKA_VALUE: + if (0 == io->u.cert.derCert.size) { + io->u.cert.derCert.data = io->derCert->data; + io->u.cert.derCert.size = io->derCert->len; + } + plog(" fetch cert CKA_VALUE\n"); + return &io->u.cert.derCert; + case CKA_ID: + plog(" fetch cert CKA_ID val=%s size=%d\n", (char *) io->id.data, + io->id.size); + return &io->id; + case CKA_TRUSTED: + plog(" fetch cert CKA_TRUSTED: returning NULL\n"); + return NULL; + default: + plog(" fetching cert unknown type %d\n", type); + break; + } + return NULL; +} + +const NSSItem * +pem_FetchPrivKeyAttribute +( + pemInternalObject * io, + CK_ATTRIBUTE_TYPE type +) +{ + PRBool isCertType = (pemCert == io->type); + pemKeyParams *kp = isCertType ? &io->u.cert.key : &io->u.key.key; + + switch (type) { + case CKA_CLASS: + return &pem_privKeyClassItem; + case CKA_TOKEN: + case CKA_LOCAL: + case CKA_SIGN: + case CKA_DECRYPT: + case CKA_SIGN_RECOVER: + return &pem_trueItem; + case CKA_SENSITIVE: + case CKA_PRIVATE: /* should move in the future */ + case CKA_MODIFIABLE: + case CKA_DERIVE: + case CKA_UNWRAP: + case CKA_EXTRACTABLE: /* will probably move in the future */ + case CKA_ALWAYS_SENSITIVE: + case CKA_NEVER_EXTRACTABLE: + return &pem_falseItem; + case CKA_KEY_TYPE: + return &pem_rsaItem; + case CKA_LABEL: + if (!isCertType) { + return &pem_emptyItem; + } + if (0 == io->u.cert.label.size) { + pem_FetchLabel(io); + } + plog(" fetch key CKA_LABEL %s\n", io->u.cert.label.data); + return &io->u.cert.label; + case CKA_SUBJECT: + if (!isCertType) { + return &pem_emptyItem; + } + plog(" fetch key CKA_SUBJECT %s\n", io->u.cert.label.data); + return &io->u.cert.subject; + case CKA_MODULUS: + if (0 == kp->modulus.size) { + pem_PopulateModulusExponent(io); + } + plog(" fetch key CKA_MODULUS\n"); + return &kp->modulus; + case CKA_PUBLIC_EXPONENT: + if (0 == kp->modulus.size) { + pem_PopulateModulusExponent(io); + } + plog(" fetch key CKA_PUBLIC_EXPONENT\n"); + return &kp->exponent; + case CKA_PRIVATE_EXPONENT: + if (0 == kp->privateExponent.size) { + pem_PopulateModulusExponent(io); + } + plog(" fetch key CKA_PRIVATE_EXPONENT\n"); + return &kp->privateExponent; + case CKA_PRIME_1: + if (0 == kp->prime1.size) { + pem_PopulateModulusExponent(io); + } + plog(" fetch key CKA_PRIME_1\n"); + return &kp->prime1; + case CKA_PRIME_2: + if (0 == kp->prime2.size) { + pem_PopulateModulusExponent(io); + } + plog(" fetch key CKA_PRIME_2\n"); + return &kp->prime2; + case CKA_EXPONENT_1: + if (0 == kp->exponent1.size) { + pem_PopulateModulusExponent(io); + } + plog(" fetch key CKA_EXPONENT_1\n"); + return &kp->exponent1; + case CKA_EXPONENT_2: + if (0 == kp->exponent2.size) { + pem_PopulateModulusExponent(io); + } + plog(" fetch key CKA_EXPONENT_2\n"); + return &kp->exponent2; + case CKA_COEFFICIENT: + if (0 == kp->coefficient.size) { + pem_PopulateModulusExponent(io); + } + plog(" fetch key CKA_COEFFICIENT_2\n"); + return &kp->coefficient; + case CKA_ID: + plog(" fetch key CKA_ID val=%s size=%d\n", (char *) io->id.data, + io->id.size); + return &io->id; + default: + return NULL; + } +} + +const NSSItem * +pem_FetchPubKeyAttribute +( + pemInternalObject * io, + CK_ATTRIBUTE_TYPE type +) +{ + PRBool isCertType = (pemCert == io->type); + pemKeyParams *kp = isCertType ? &io->u.cert.key : &io->u.key.key; + + switch (type) { + case CKA_CLASS: + return &pem_pubKeyClassItem; + case CKA_TOKEN: + case CKA_LOCAL: + case CKA_ENCRYPT: + case CKA_VERIFY: + case CKA_VERIFY_RECOVER: + return &pem_trueItem; + case CKA_PRIVATE: + case CKA_MODIFIABLE: + case CKA_DERIVE: + case CKA_WRAP: + return &pem_falseItem; + case CKA_KEY_TYPE: + return &pem_rsaItem; + case CKA_LABEL: + if (!isCertType) { + return &pem_emptyItem; + } + if (0 == io->u.cert.label.size) { + pem_FetchLabel(io); + } + return &io->u.cert.label; + case CKA_SUBJECT: + if (!isCertType) { + return &pem_emptyItem; + } + return &io->u.cert.subject; + case CKA_MODULUS: + if (0 == kp->modulus.size) { + pem_PopulateModulusExponent(io); + } + return &kp->modulus; + case CKA_PUBLIC_EXPONENT: + if (0 == kp->modulus.size) { + pem_PopulateModulusExponent(io); + } + return &kp->exponent; + case CKA_ID: + return &io->id; + default: + break; + } + return NULL; +} + +const NSSItem * +pem_FetchTrustAttribute +( + pemInternalObject * io, + CK_ATTRIBUTE_TYPE type +) +{ + static NSSItem hash; + SECStatus rv; + + switch (type) { + case CKA_CLASS: + return &pem_trustClassItem; + case CKA_TOKEN: + return &pem_trueItem; + case CKA_PRIVATE: + return &pem_falseItem; + case CKA_CERTIFICATE_TYPE: + return &pem_x509Item; + case CKA_LABEL: + if (0 == io->u.cert.label.size) { + pem_FetchLabel(io); + } + plog(" fetch trust CKA_LABEL %s\n", io->u.cert.label.data); + return &io->u.cert.label; + case CKA_SUBJECT: + plog(" fetch trust CKA_SUBJECT\n"); + return NULL; + case CKA_ISSUER: + plog(" fetch trust CKA_ISSUER\n"); + return &io->u.cert.issuer; + case CKA_SERIAL_NUMBER: + plog(" fetch trust CKA_SERIAL_NUMBER size %d value %08x\n", io->u.cert.serial.size, io->u.cert.serial.data); + return &io->u.cert.serial; + case CKA_VALUE: + return &pem_trueItem; + case CKA_ID: + plog(" fetch trust CKA_ID val=%s size=%d\n", (char *) io->id.data, + io->id.size); + return &io->id; + case CKA_TRUSTED: + return &pem_trusted; + case CKA_TRUST_SERVER_AUTH: + return &pem_trusted; + case CKA_TRUST_CLIENT_AUTH: + return &pem_trusted; + case CKA_TRUST_CODE_SIGNING: + return &pem_trusted; + case CKA_TRUST_EMAIL_PROTECTION: + return &pem_trusted; + case CKA_TRUST_IPSEC_END_SYSTEM: + return &pem_trusted; + case CKA_TRUST_IPSEC_TUNNEL: + return &pem_trusted; + case CKA_TRUST_IPSEC_USER: + return &pem_trusted; + case CKA_TRUST_TIME_STAMPING: + return &pem_trusted; + case CKA_TRUST_STEP_UP_APPROVED: + return &pem_falseItem; + case CKA_CERT_SHA1_HASH: + hash.size = 0; + hash.data = NULL; + nsslibc_memset(io->u.cert.sha1_hash, 0, SHA1_LENGTH); + rv = SHA1_HashBuf(io->u.cert.sha1_hash, io->derCert->data, + io->derCert->len); + if (rv == SECSuccess) { + hash.data = io->u.cert.sha1_hash; + hash.size = sizeof(io->u.cert.sha1_hash); + } + return &hash; + case CKA_CERT_MD5_HASH: + hash.size = 0; + hash.data = NULL; + nsslibc_memset(io->u.cert.sha1_hash, 0, MD5_LENGTH); + rv = MD5_HashBuf(io->u.cert.sha1_hash, io->derCert->data, + io->derCert->len); + if (rv == SECSuccess) { + hash.data = io->u.cert.sha1_hash; + hash.size = sizeof(io->u.cert.sha1_hash); + } + return &hash; + default: + return &pem_trusted; + break; + } + return NULL; +} + +const NSSItem * +pem_FetchAttribute +( + pemInternalObject * io, + CK_ATTRIBUTE_TYPE type +) +{ + CK_ULONG i; + + if (io->type == pemRaw) { + for (i = 0; i < io->u.raw.n; i++) { + if (type == io->u.raw.types[i]) { + return &io->u.raw.items[i]; + } + } + return NULL; + } + /* deal with the common attributes */ + switch (io->objClass) { + case CKO_CERTIFICATE: + return pem_FetchCertAttribute(io, type); + case CKO_PRIVATE_KEY: + return pem_FetchPrivKeyAttribute(io, type); + case CKO_NETSCAPE_TRUST: + return pem_FetchTrustAttribute(io, type); + case CKO_PUBLIC_KEY: + return pem_FetchPubKeyAttribute(io, type); + } + return NULL; +} + +void +pem_DestroyInternalObject +( + pemInternalObject * io +) +{ + switch (io->type) { + case pemRaw: + return; + case pemCert: + nss_ZFreeIf(io->u.cert.labelData); + nss_ZFreeIf(io->u.cert.key.privateKey); + nss_ZFreeIf(io->u.cert.key.pubKey); + nss_ZFreeIf(io->idData); + nss_ZFreeIf(io->nickname); + nss_ZFreeIf(io->derCert); + if (io->u.cert.subject.size > 0) { + PR_Free(io->u.cert.subject.data); + } + if (io->u.cert.issuer.size > 0) { + PR_Free(io->u.cert.issuer.data); + } + if (io->u.cert.serial.size > 0) { + PR_Free(io->u.cert.serial.data); + } + break; + case pemBareKey: + nss_ZFreeIf(io->u.key.key.privateKey); + nss_ZFreeIf(io->u.key.key.pubKey); + nss_ZFreeIf(io->idData); + nss_ZFreeIf(io->nickname); + nss_ZFreeIf(io->derCert); + + /* strdup'd in ReadDERFromFile */ + if (io->u.key.ivstring) + free(io->u.key.ivstring); + break; + case pemTrust: + nss_ZFreeIf(io->idData); + nss_ZFreeIf(io->nickname); + nss_ZFreeIf(io->derCert); + } + nss_ZFreeIf(io); + return; +} + +/* + * Finalize - unneeded + * Destroy - CKR_SESSION_READ_ONLY + * IsTokenObject - CK_TRUE + * GetAttributeCount + * GetAttributeTypes + * GetAttributeSize + * GetAttribute + * SetAttribute - unneeded + * GetObjectSize + */ + +static CK_RV +pem_mdObject_Destroy +( + NSSCKMDObject * mdObject, + NSSCKFWObject * fwObject, + NSSCKMDSession * mdSession, + NSSCKFWSession * fwSession, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance +) +{ + pemInternalObject *io = (pemInternalObject *) mdObject->etc; + + pem_DestroyInternalObject(io); + return CKR_OK; +} + +static CK_BBOOL +pem_mdObject_IsTokenObject +( + NSSCKMDObject * mdObject, + NSSCKFWObject * fwObject, + NSSCKMDSession * mdSession, + NSSCKFWSession * fwSession, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance +) +{ + return CK_TRUE; +} + +static CK_ULONG +pem_mdObject_GetAttributeCount +( + NSSCKMDObject * mdObject, + NSSCKFWObject * fwObject, + NSSCKMDSession * mdSession, + NSSCKFWSession * fwSession, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_RV * pError +) +{ + pemInternalObject *io = (pemInternalObject *) mdObject->etc; + + if (pemRaw == io->type) { + return io->u.raw.n; + } + switch (io->objClass) { + case CKO_CERTIFICATE: + return certAttrsCount; + case CKO_PUBLIC_KEY: + return pubKeyAttrsCount; + case CKO_PRIVATE_KEY: + return privKeyAttrsCount; + case CKO_NETSCAPE_TRUST: + return trustAttrsCount; + default: + break; + } + return 0; +} + +static CK_RV +pem_mdObject_GetAttributeTypes +( + NSSCKMDObject * mdObject, + NSSCKFWObject * fwObject, + NSSCKMDSession * mdSession, + NSSCKFWSession * fwSession, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_ATTRIBUTE_TYPE_PTR typeArray, + CK_ULONG ulCount +) +{ + pemInternalObject *io = (pemInternalObject *) mdObject->etc; + CK_ULONG i; + CK_RV error = CKR_OK; + const CK_ATTRIBUTE_TYPE *attrs = NULL; + CK_ULONG size = + pem_mdObject_GetAttributeCount(mdObject, fwObject, mdSession, + fwSession, mdToken, fwToken, mdInstance, + fwInstance, &error); + + if (size != ulCount) { + return CKR_BUFFER_TOO_SMALL; + } + if (io->type == pemRaw) { + attrs = io->u.raw.types; + } else + switch (io->objClass) { + case CKO_CERTIFICATE: + attrs = certAttrs; + break; + case CKO_PUBLIC_KEY: + attrs = pubKeyAttrs; + break; + case CKO_PRIVATE_KEY: + attrs = privKeyAttrs; + break; + default: + return CKR_OK; + } + + for (i = 0; i < size; i++) { + typeArray[i] = attrs[i]; + } + + return CKR_OK; +} + +static CK_ULONG +pem_mdObject_GetAttributeSize +( + NSSCKMDObject * mdObject, + NSSCKFWObject * fwObject, + NSSCKMDSession * mdSession, + NSSCKFWSession * fwSession, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_ATTRIBUTE_TYPE attribute, + CK_RV * pError +) +{ + pemInternalObject *io = (pemInternalObject *) mdObject->etc; + + const NSSItem *b; + + b = pem_FetchAttribute(io, attribute); + + if ((const NSSItem *) NULL == b) { + *pError = CKR_ATTRIBUTE_TYPE_INVALID; + return 0; + } + return b->size; +} + +static NSSCKFWItem +pem_mdObject_GetAttribute +( + NSSCKMDObject * mdObject, + NSSCKFWObject * fwObject, + NSSCKMDSession * mdSession, + NSSCKFWSession * fwSession, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_ATTRIBUTE_TYPE attribute, + CK_RV * pError +) +{ + NSSCKFWItem mdItem; + pemInternalObject *io = (pemInternalObject *) mdObject->etc; + + mdItem.needsFreeing = PR_FALSE; + mdItem.item = (NSSItem *) pem_FetchAttribute(io, attribute); + + if ((NSSItem *) NULL == mdItem.item) { + *pError = CKR_ATTRIBUTE_TYPE_INVALID; + } + + return mdItem; +} + +/* + * get an attribute from a template. Value is returned in NSS item. + * data for the item is owned by the template. + */ +CK_RV +pem_GetAttribute +( + CK_ATTRIBUTE_TYPE type, + CK_ATTRIBUTE * template, + CK_ULONG templateSize, + NSSItem * item +) +{ + CK_ULONG i; + + for (i = 0; i < templateSize; i++) { + if (template[i].type == type) { + item->data = template[i].pValue; + item->size = template[i].ulValueLen; + return CKR_OK; + } + } + return CKR_TEMPLATE_INCOMPLETE; +} + +/* + * get an attribute which is type CK_ULONG. + */ +CK_ULONG +pem_GetULongAttribute +( + CK_ATTRIBUTE_TYPE type, + CK_ATTRIBUTE * template, + CK_ULONG templateSize, + CK_RV * pError +) +{ + NSSItem item; + + *pError = pem_GetAttribute(type, template, templateSize, &item); + if (CKR_OK != *pError) { + return (CK_ULONG) 0; + } + if (item.size != sizeof(CK_ULONG)) { + *pError = CKR_ATTRIBUTE_VALUE_INVALID; + return (CK_ULONG) 0; + } + return *(CK_ULONG *) item.data; +} + +/* + * get an attribute which is type CK_BBOOL. + */ +CK_BBOOL +pem_GetBoolAttribute +( + CK_ATTRIBUTE_TYPE type, + CK_ATTRIBUTE * template, + CK_ULONG templateSize, + CK_RV * pError +) +{ + NSSItem item; + + *pError = pem_GetAttribute(type, template, templateSize, &item); + if (CKR_OK != *pError) { + return (CK_BBOOL) 0; + } + if (item.size != sizeof(CK_BBOOL)) { + *pError = CKR_ATTRIBUTE_VALUE_INVALID; + return (CK_BBOOL) 0; + } + return *(CK_BBOOL *) item.data; +} + +/* + * Get a string attribute. Caller needs to free this. + */ +char * +pem_GetStringAttribute +( + CK_ATTRIBUTE_TYPE type, + CK_ATTRIBUTE * template, + CK_ULONG templateSize, + CK_RV * pError +) +{ + NSSItem item; + char *str; + + /* get the attribute */ + *pError = pem_GetAttribute(type, template, templateSize, &item); + if (CKR_OK != *pError) { + return (char *) NULL; + } + /* make sure it is null terminated */ + str = nss_ZNEWARRAY(NULL, char, item.size + 1); + if ((char *) NULL == str) { + *pError = CKR_HOST_MEMORY; + return (char *) NULL; + } + + nsslibc_memcpy(str, item.data, item.size); + str[item.size] = 0; + + return str; +} + +static CK_ULONG +pem_mdObject_GetObjectSize +( + NSSCKMDObject * mdObject, + NSSCKFWObject * fwObject, + NSSCKMDSession * mdSession, + NSSCKFWSession * fwSession, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_RV * pError +) +{ + pemInternalObject *io = (pemInternalObject *) mdObject->etc; + CK_ULONG rv = 1; + + /* size is irrelevant to this token */ + return rv; +} + +static const NSSCKMDObject +pem_prototype_mdObject = { + (void *) NULL, /* etc */ + NULL, /* Finalize */ + pem_mdObject_Destroy, + pem_mdObject_IsTokenObject, + pem_mdObject_GetAttributeCount, + pem_mdObject_GetAttributeTypes, + pem_mdObject_GetAttributeSize, + pem_mdObject_GetAttribute, + NULL, /* FreeAttribute */ + NULL, /* SetAttribute */ + pem_mdObject_GetObjectSize, + (void *) NULL /* null terminator */ +}; + +NSS_IMPLEMENT NSSCKMDObject * +pem_CreateMDObject +( + NSSArena * arena, + pemInternalObject * io, + CK_RV * pError +) +{ + if ((void *) NULL == io->mdObject.etc) { + (void) nsslibc_memcpy(&io->mdObject, &pem_prototype_mdObject, + sizeof(pem_prototype_mdObject)); + io->mdObject.etc = (void *) io; + } + + return &io->mdObject; +} + +/* + * Each object has an identifier. For a certificate and key pair this id + * needs to be the same so we use the right combination. If the target object + * is a key we first look to see if its certificate was already added and if + * so, use that id. The same thing is done when a key is added. + */ +NSS_EXTERN NSSCKMDObject * +pem_CreateObject +( + NSSCKFWInstance * fwInstance, + NSSCKFWSession * fwSession, + NSSCKMDToken * mdToken, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_RV * pError +) +{ + CK_OBJECT_CLASS objClass; + pemInternalObject *io = NULL; + CK_BBOOL isToken; + NSSCKFWSlot *fwSlot; + CK_SLOT_ID slotID; + CK_BBOOL cacert; + char *filename; + SECItem **derlist = NULL; + int nobjs, i; + int objid, count, size; + pemToken *token; + int cipher; + char *ivstring = NULL; + + count = pem_nobjs; + if (count > 0) + size = ((count / PEM_ITEM_CHUNK) + 1) * PEM_ITEM_CHUNK; + else + size = 0; + + /* + * only create token objects + */ + isToken = pem_GetBoolAttribute(CKA_TOKEN, pTemplate, + ulAttributeCount, pError); + if (CKR_OK != *pError) { + return (NSSCKMDObject *) NULL; + } + if (!isToken) { + *pError = CKR_ATTRIBUTE_VALUE_INVALID; + return (NSSCKMDObject *) NULL; + } + + /* What slot are we adding the object to? */ + fwSlot = nssCKFWSession_GetFWSlot(fwSession); + if ((NSSCKFWSlot *) NULL == fwSlot) { + *pError = CKR_ATTRIBUTE_VALUE_INVALID; + *pError = CKR_GENERAL_ERROR; + return (NSSCKMDObject *) NULL; + + } + slotID = nssCKFWSlot_GetSlotID(fwSlot); + + token = (pemToken *) mdToken->etc; + + /* + * only create keys and certs. + */ + objClass = pem_GetULongAttribute(CKA_CLASS, pTemplate, + ulAttributeCount, pError); + if (CKR_OK != *pError) { + return (NSSCKMDObject *) NULL; + } + + cacert = pem_GetBoolAttribute(CKA_TRUST, pTemplate, + ulAttributeCount, pError); + + filename = pem_GetStringAttribute(CKA_LABEL, pTemplate, + ulAttributeCount, pError); + if (CKR_OK != *pError) { + return (NSSCKMDObject *) NULL; + } + +#ifdef notdef + if (objClass == CKO_PUBLIC_KEY) { + return CKR_OK; /* fake public key creation, happens as a side effect of + * private key creation */ + } +#endif + if (objClass == CKO_CERTIFICATE) { + int i; + + nobjs = ReadDERFromFile(&derlist, filename, PR_TRUE, &cipher, &ivstring, PR_TRUE /* certs only */); + if (nobjs < 1) + return (NSSCKMDObject *) NULL; + + objid = -1; + /* Brute force: find the id of the key, if any, in this slot */ + for (i = 0; i < pem_nobjs; i++) { + if ((slotID == gobj[i]->slotID) + && (gobj[i]->type == pemBareKey)) { + objid = atoi(gobj[i]->id.data); + } + } + + if (objid == -1) { + /* We're just adding a cert, we'll assume the key is next */ + objid = pem_nobjs + 1; + } + + if (cacert) { + /* Add the certificate. There may be more than one */ + int c; + for (c = 0; c < nobjs; c++) { + char nickname[1024]; + objid = pem_nobjs + 1; + + snprintf(nickname, 1024, "%s - %d", filename, c); + + io = (pemInternalObject *) CreateObject(CKO_CERTIFICATE, + pemCert, derlist[c], + NULL, nickname, 0, + slotID); + if (io == NULL) + goto loser; + PUT_Object(io, *pError); + pem_nobjs++; + + /* Add the trust object */ + io = CreateObject(CKO_NETSCAPE_TRUST, pemTrust, derlist[c], + NULL, nickname, 0, slotID); + if (io == NULL) + goto loser; + + PUT_Object(io, *pError); + pem_nobjs++; + } + } else { + io = (pemInternalObject *) CreateObject(CKO_CERTIFICATE, + pemCert, derlist[0], + NULL, filename, objid, + slotID); + if (io == NULL) + goto loser; + PUT_Object(io, *pError); + pem_nobjs++; + } + } else if (objClass == CKO_PRIVATE_KEY) { + /* Brute force: find the id of the certificate, if any, in this slot */ + int i; + SECItem certDER; + CK_SESSION_HANDLE hSession; + + nobjs = ReadDERFromFile(&derlist, filename, PR_TRUE, &cipher, &ivstring, PR_FALSE /* keys only */); + if (nobjs < 1) + return (NSSCKMDObject *) NULL; + + certDER.len = 0; /* in case there is no equivalent cert */ + + objid = -1; + for (i = 0; i < pem_nobjs; i++) { + if ((slotID == gobj[i]->slotID) && (gobj[i]->type == pemCert)) { + objid = atoi(gobj[i]->id.data); + certDER.data = + (void *) nss_ZAlloc(NULL, gobj[i]->derCert->len); + certDER.len = gobj[i]->derCert->len; + nsslibc_memcpy(certDER.data, gobj[i]->derCert->data, + gobj[i]->derCert->len); + } + } + + /* We're just adding a key, we'll assume the cert is next */ + if (objid == -1) + objid = pem_nobjs + 1; + + io = (pemInternalObject *) CreateObject(CKO_PRIVATE_KEY, + pemBareKey, &certDER, + derlist[0], filename, + objid, slotID); + if (io == NULL) + goto loser; + io->u.key.ivstring = ivstring; + io->u.key.cipher = cipher; + PUT_Object(io, *pError); + pem_nobjs++; + nss_ZFreeIf(certDER.data); + + /* If the key was encrypted then free the session to make it appear that + * the token was removed so we can force a login. + */ + if (cipher) { + PRIntervalTime onesec = PR_SecondsToInterval(1); + token_needsLogin[slotID - 1] = PR_TRUE; + + /* We have to sleep so that NSS will notice that the token was + * removed. + */ + PR_Sleep(onesec); + hSession = + nssCKFWInstance_FindSessionHandle(fwInstance, fwSession); + nssCKFWInstance_DestroySessionHandle(fwInstance, hSession); + } else { + *pError = CKR_KEY_UNEXTRACTABLE; + } + } else { + *pError = CKR_ATTRIBUTE_VALUE_INVALID; + } + + loser: + + for (i = 0; i < nobjs; i++) { + free(derlist[i]->data); + free(derlist[i]); + } + nss_ZFreeIf(filename); + nss_ZFreeIf(derlist); + if ((pemInternalObject *) NULL == io) { + return (NSSCKMDObject *) NULL; + } + return pem_CreateMDObject(NULL, io, pError); +} diff --git a/prsa.c b/prsa.c new file mode 100644 index 0000000..1391dd3 --- /dev/null +++ b/prsa.c @@ -0,0 +1,615 @@ +/* ***** 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. + * + * Contributor(s): + * Rob Crittenden (rcritten@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 "ckpem.h" +#include "secdert.h" +#include "secoid.h" +#include "nssckmdt.h" + +#define SSL3_SHAMD5_HASH_SIZE 36 /* LEN_MD5 (16) + LEN_SHA1 (20) */ + +/* + * prsa.c + * + * This file implements the NSSCKMDMechnaism and NSSCKMDCryptoOperation objects + * for the RSA operation. + */ + +const SEC_ASN1Template pem_RSAPrivateKeyTemplate[] = { + {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(pemLOWKEYPrivateKey)} , + {SEC_ASN1_INTEGER, offsetof(pemLOWKEYPrivateKey, u.rsa.version)}, + {SEC_ASN1_INTEGER, offsetof(pemLOWKEYPrivateKey, u.rsa.modulus)}, + {SEC_ASN1_INTEGER, offsetof(pemLOWKEYPrivateKey, u.rsa.publicExponent)}, + {SEC_ASN1_INTEGER, offsetof(pemLOWKEYPrivateKey, u.rsa.privateExponent)}, + {SEC_ASN1_INTEGER, offsetof(pemLOWKEYPrivateKey, u.rsa.prime1)}, + {SEC_ASN1_INTEGER, offsetof(pemLOWKEYPrivateKey, u.rsa.prime2)}, + {SEC_ASN1_INTEGER, offsetof(pemLOWKEYPrivateKey, u.rsa.exponent1)}, + {SEC_ASN1_INTEGER, offsetof(pemLOWKEYPrivateKey, u.rsa.exponent2)}, + {SEC_ASN1_INTEGER, offsetof(pemLOWKEYPrivateKey, u.rsa.coefficient)}, + {0} +}; + +/* Declarations */ +SECStatus pem_RSA_Sign(pemLOWKEYPrivateKey * key, unsigned char *output, + unsigned int *outputLen, unsigned int maxOutputLen, + unsigned char *input, unsigned int inputLen); +SECStatus pem_RSA_DecryptBlock(pemLOWKEYPrivateKey * key, + unsigned char *output, unsigned int *outputLen, + unsigned int maxOutputLen, unsigned char *input, + unsigned int inputLen); + +void prepare_low_rsa_priv_key_for_asn1(pemLOWKEYPrivateKey * key) +{ + key->u.rsa.modulus.type = siUnsignedInteger; + key->u.rsa.publicExponent.type = siUnsignedInteger; + key->u.rsa.privateExponent.type = siUnsignedInteger; + key->u.rsa.prime1.type = siUnsignedInteger; + key->u.rsa.prime2.type = siUnsignedInteger; + key->u.rsa.exponent1.type = siUnsignedInteger; + key->u.rsa.exponent2.type = siUnsignedInteger; + key->u.rsa.coefficient.type = siUnsignedInteger; +} + +unsigned int +pem_PrivateModulusLen(pemLOWKEYPrivateKey * privk) +{ + + unsigned char b0; + + switch (privk->keyType) { + case pemLOWKEYRSAKey: + b0 = privk->u.rsa.modulus.data[0]; + return b0 ? privk->u.rsa.modulus.len : privk->u.rsa.modulus.len - + 1; + default: + break; + } + return 0; +} + +struct SFTKHashSignInfoStr { + SECOidTag hashOid; + pemLOWKEYPrivateKey *key; +}; +typedef struct SFTKHashSignInfoStr SFTKHashSignInfo; + +void +pem_DestroyPrivateKey(pemLOWKEYPrivateKey * privk) +{ + if (privk && privk->arena) { + PORT_FreeArena(privk->arena, PR_TRUE); + } +} + +void +pem_PopulateModulusExponent(pemInternalObject * io) +{ + const NSSItem *classItem = pem_FetchAttribute(io, CKA_CLASS); + const NSSItem *keyType = pem_FetchAttribute(io, CKA_KEY_TYPE); + pemLOWKEYPrivateKey *lpk = NULL; + PLArenaPool *arena; + SECStatus rv; + + arena = PORT_NewArena(2048); + if (!arena) { + return; + } + + /* make sure we have the right objects */ + if (((const NSSItem *) NULL == classItem) || + (sizeof(CK_OBJECT_CLASS) != classItem->size) || + (CKO_PRIVATE_KEY != *(CK_OBJECT_CLASS *) classItem->data) || + ((const NSSItem *) NULL == keyType) || + (sizeof(CK_KEY_TYPE) != keyType->size) || + (CKK_RSA != *(CK_KEY_TYPE *) keyType->data)) { + return; + } + + lpk = (pemLOWKEYPrivateKey *) nss_ZAlloc(NULL, + sizeof(pemLOWKEYPrivateKey)); + if (lpk == NULL) { + PORT_FreeArena(arena, PR_FALSE); + return; + } + + lpk->arena = arena; + lpk->keyType = pemLOWKEYRSAKey; + prepare_low_rsa_priv_key_for_asn1(lpk); + + /* decode the private key and any algorithm parameters */ + rv = SEC_QuickDERDecodeItem(arena, lpk, pem_RSAPrivateKeyTemplate, + io->u.key.key.privateKey); + + if (rv != SECSuccess) { + PORT_FreeArena(arena, PR_FALSE); + return; + } + + io->u.key.key.modulus.data = + (void *) nss_ZAlloc(NULL, lpk->u.rsa.modulus.len); + io->u.key.key.modulus.size = lpk->u.rsa.modulus.len; + nsslibc_memcpy(io->u.key.key.modulus.data, lpk->u.rsa.modulus.data, + lpk->u.rsa.modulus.len); + + io->u.key.key.exponent.data = + (void *) nss_ZAlloc(NULL, lpk->u.rsa.publicExponent.len); + io->u.key.key.exponent.size = lpk->u.rsa.publicExponent.len; + nsslibc_memcpy(io->u.key.key.exponent.data, + lpk->u.rsa.publicExponent.data, + lpk->u.rsa.publicExponent.len); + + io->u.key.key.privateExponent.data = + (void *) nss_ZAlloc(NULL, lpk->u.rsa.privateExponent.len); + io->u.key.key.privateExponent.size = lpk->u.rsa.privateExponent.len; + nsslibc_memcpy(io->u.key.key.privateExponent.data, + lpk->u.rsa.privateExponent.data, + lpk->u.rsa.privateExponent.len); + + io->u.key.key.prime1.data = + (void *) nss_ZAlloc(NULL, lpk->u.rsa.prime1.len); + io->u.key.key.prime1.size = lpk->u.rsa.prime1.len; + nsslibc_memcpy(io->u.key.key.prime1.data, lpk->u.rsa.prime1.data, + lpk->u.rsa.prime1.len); + + io->u.key.key.prime2.data = + (void *) nss_ZAlloc(NULL, lpk->u.rsa.prime2.len); + io->u.key.key.prime2.size = lpk->u.rsa.prime2.len; + nsslibc_memcpy(io->u.key.key.prime2.data, lpk->u.rsa.prime2.data, + lpk->u.rsa.prime2.len); + + io->u.key.key.exponent1.data = + (void *) nss_ZAlloc(NULL, lpk->u.rsa.exponent1.len); + io->u.key.key.exponent1.size = lpk->u.rsa.exponent1.len; + nsslibc_memcpy(io->u.key.key.exponent1.data, lpk->u.rsa.exponent1.data, + lpk->u.rsa.exponent1.len); + + io->u.key.key.exponent2.data = + (void *) nss_ZAlloc(NULL, lpk->u.rsa.exponent2.len); + io->u.key.key.exponent2.size = lpk->u.rsa.exponent2.len; + nsslibc_memcpy(io->u.key.key.exponent2.data, lpk->u.rsa.exponent2.data, + lpk->u.rsa.exponent2.len); + + io->u.key.key.coefficient.data = + (void *) nss_ZAlloc(NULL, lpk->u.rsa.coefficient.len); + io->u.key.key.coefficient.size = lpk->u.rsa.coefficient.len; + nsslibc_memcpy(io->u.key.key.coefficient.data, + lpk->u.rsa.coefficient.data, + lpk->u.rsa.coefficient.len); + + pem_DestroyPrivateKey(lpk); + return; +} + +typedef struct pemInternalCryptoOperationRSAPrivStr + pemInternalCryptoOperationRSAPriv; +struct pemInternalCryptoOperationRSAPrivStr +{ + NSSCKMDCryptoOperation mdOperation; + NSSCKMDMechanism *mdMechanism; + pemInternalObject *iKey; + pemLOWKEYPrivateKey *lpk; + NSSItem *buffer; +}; + +/* + * pem_mdCryptoOperationRSAPriv_Create + */ +static NSSCKMDCryptoOperation * +pem_mdCryptoOperationRSAPriv_Create +( + const NSSCKMDCryptoOperation * proto, + NSSCKMDMechanism * mdMechanism, + NSSCKMDObject * mdKey, + CK_RV * pError +) +{ + pemInternalObject *iKey = (pemInternalObject *) mdKey->etc; + const NSSItem *classItem = pem_FetchAttribute(iKey, CKA_CLASS); + const NSSItem *keyType = pem_FetchAttribute(iKey, CKA_KEY_TYPE); + pemInternalCryptoOperationRSAPriv *iOperation; + pemLOWKEYPrivateKey *lpk = NULL; + PLArenaPool *arena; + SECStatus rv; + + arena = PORT_NewArena(2048); + if (!arena) { + *pError = CKR_HOST_MEMORY; + return (NSSCKMDCryptoOperation *) NULL; + } + + /* make sure we have the right objects */ + if (((const NSSItem *) NULL == classItem) || + (sizeof(CK_OBJECT_CLASS) != classItem->size) || + (CKO_PRIVATE_KEY != *(CK_OBJECT_CLASS *) classItem->data) || + ((const NSSItem *) NULL == keyType) || + (sizeof(CK_KEY_TYPE) != keyType->size) || + (CKK_RSA != *(CK_KEY_TYPE *) keyType->data)) { + *pError = CKR_KEY_TYPE_INCONSISTENT; + return (NSSCKMDCryptoOperation *) NULL; + } + + lpk = (pemLOWKEYPrivateKey *) nss_ZAlloc(NULL, + sizeof (pemLOWKEYPrivateKey)); + if (lpk == NULL) { + *pError = CKR_HOST_MEMORY; + return (NSSCKMDCryptoOperation *) NULL; + } + lpk->arena = arena; + lpk->keyType = pemLOWKEYRSAKey; + prepare_low_rsa_priv_key_for_asn1(lpk); + iKey->u.key.key.modulus.data = + (void *) nss_ZAlloc(NULL, lpk->u.rsa.modulus.len); + iKey->u.key.key.modulus.size = lpk->u.rsa.modulus.len; + nsslibc_memcpy(iKey->u.key.key.modulus.data, lpk->u.rsa.modulus.data, + lpk->u.rsa.modulus.len); + + /* decode the private key and any algorithm parameters */ + rv = SEC_QuickDERDecodeItem(arena, lpk, pem_RSAPrivateKeyTemplate, + iKey->u.key.key.privateKey); + + if (rv != SECSuccess) { + PORT_FreeArena(arena, PR_FALSE); + *pError = CKR_HOST_MEMORY; + return (NSSCKMDCryptoOperation *) NULL; + } + + iOperation = nss_ZNEW(NULL, pemInternalCryptoOperationRSAPriv); + if ((pemInternalCryptoOperationRSAPriv *) NULL == iOperation) { + *pError = CKR_HOST_MEMORY; + return (NSSCKMDCryptoOperation *) NULL; + } + iOperation->mdMechanism = mdMechanism; + iOperation->iKey = iKey; + iOperation->lpk = lpk; + + nsslibc_memcpy(&iOperation->mdOperation, + proto, sizeof(NSSCKMDCryptoOperation)); + iOperation->mdOperation.etc = iOperation; + + return &iOperation->mdOperation; +} + +static CK_RV +pem_mdCryptoOperationRSAPriv_Destroy +( + NSSCKMDCryptoOperation * mdOperation, + NSSCKFWCryptoOperation * fwOperation, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance +) +{ + pemInternalCryptoOperationRSAPriv *iOperation = + (pemInternalCryptoOperationRSAPriv *) mdOperation->etc; + + if (iOperation->buffer) { + nssItem_Destroy(iOperation->buffer); + } + nss_ZFreeIf(iOperation); + return CKR_OK; +} + +static CK_ULONG +pem_mdCryptoOperationRSA_GetFinalLength +( + NSSCKMDCryptoOperation * mdOperation, + NSSCKFWCryptoOperation * fwOperation, + NSSCKMDSession * mdSession, + NSSCKFWSession * fwSession, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_RV * pError +) +{ + pemInternalCryptoOperationRSAPriv *iOperation = + (pemInternalCryptoOperationRSAPriv *) mdOperation->etc; + const NSSItem *modulus = + pem_FetchAttribute(iOperation->iKey, CKA_MODULUS); + + return modulus->size; +} + + +/* + * pem_mdCryptoOperationRSADecrypt_GetOperationLength + * we won't know the length until we actually decrypt the + * input block. Since we go to all the work to decrypt the + * the block, we'll save if for when the block is asked for + */ +static CK_ULONG +pem_mdCryptoOperationRSADecrypt_GetOperationLength +( + NSSCKMDCryptoOperation * mdOperation, + NSSCKFWCryptoOperation * fwOperation, + NSSCKMDSession * mdSession, + NSSCKFWSession * fwSession, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + const NSSItem * input, + CK_RV * pError +) +{ + pemInternalCryptoOperationRSAPriv *iOperation = + (pemInternalCryptoOperationRSAPriv *) mdOperation->etc; + SECStatus rv; + + /* FIXME: Just because Microsoft is broken doesn't mean I have to be. + * but this is faster to do for now */ + + /* Microsoft's Decrypt operation works in place. Since we don't want + * to trash our input buffer, we make a copy of it */ + iOperation->buffer = nssItem_Duplicate((NSSItem *) input, NULL, NULL); + if ((NSSItem *) NULL == iOperation->buffer) { + *pError = CKR_HOST_MEMORY; + return 0; + } + + rv = pem_RSA_DecryptBlock(iOperation->lpk, iOperation->buffer->data, + &iOperation->buffer->size, + iOperation->buffer->size, input->data, + input->size); + + if (rv != SECSuccess) { + return 0; + } + + return iOperation->buffer->size; +} + +/* + * pem_mdCryptoOperationRSADecrypt_UpdateFinal + * + * NOTE: pem_mdCryptoOperationRSADecrypt_GetOperationLength is presumed to + * have been called previously. + */ +static CK_RV +pem_mdCryptoOperationRSADecrypt_UpdateFinal +( + NSSCKMDCryptoOperation * mdOperation, + NSSCKFWCryptoOperation * fwOperation, + NSSCKMDSession * mdSession, + NSSCKFWSession * fwSession, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + const NSSItem * input, + NSSItem * output +) +{ + pemInternalCryptoOperationRSAPriv *iOperation = + (pemInternalCryptoOperationRSAPriv *) mdOperation->etc; + NSSItem *buffer = iOperation->buffer; + + if ((NSSItem *) NULL == buffer) { + return CKR_GENERAL_ERROR; + } + nsslibc_memcpy(output->data, buffer->data, buffer->size); + output->size = buffer->size; + return CKR_OK; +} + +/* + * pem_mdCryptoOperationRSASign_UpdateFinal + * + */ +static CK_RV +pem_mdCryptoOperationRSASign_UpdateFinal +( + NSSCKMDCryptoOperation * mdOperation, + NSSCKFWCryptoOperation * fwOperation, + NSSCKMDSession * mdSession, + NSSCKFWSession * fwSession, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + const NSSItem * input, + NSSItem * output +) +{ + pemInternalCryptoOperationRSAPriv *iOperation = + (pemInternalCryptoOperationRSAPriv *) mdOperation->etc; + CK_RV error = CKR_OK; + SECStatus rv = SECSuccess; + + rv = pem_RSA_Sign(iOperation->lpk, output->data, &output->size, + output->size, input->data, input->size); + + if (rv != SECSuccess) { + error = CKR_GENERAL_ERROR; + } + + return error; +} + +NSS_IMPLEMENT_DATA const NSSCKMDCryptoOperation +pem_mdCryptoOperationRSADecrypt_proto = { + NULL, /* etc */ + pem_mdCryptoOperationRSAPriv_Destroy, + NULL, /* GetFinalLengh - not needed for one shot Decrypt/Encrypt */ + pem_mdCryptoOperationRSADecrypt_GetOperationLength, + NULL, /* Final - not needed for one shot operation */ + NULL, /* Update - not needed for one shot operation */ + NULL, /* DigestUpdate - not needed for one shot operation */ + pem_mdCryptoOperationRSADecrypt_UpdateFinal, + NULL, /* UpdateCombo - not needed for one shot operation */ + NULL, /* DigestKey - not needed for one shot operation */ + (void *) NULL /* null terminator */ +}; + +NSS_IMPLEMENT_DATA const NSSCKMDCryptoOperation +pem_mdCryptoOperationRSASign_proto = { + NULL, /* etc */ + pem_mdCryptoOperationRSAPriv_Destroy, + pem_mdCryptoOperationRSA_GetFinalLength, + NULL, /* GetOperationLengh - not needed for one shot Sign/Verify */ + NULL, /* Final - not needed for one shot operation */ + NULL, /* Update - not needed for one shot operation */ + NULL, /* DigestUpdate - not needed for one shot operation */ + pem_mdCryptoOperationRSASign_UpdateFinal, + NULL, /* UpdateCombo - not needed for one shot operation */ + NULL, /* DigestKey - not needed for one shot operation */ + (void *) NULL /* null terminator */ +}; + +/********** NSSCKMDMechansim functions ***********************/ +/* + * pem_mdMechanismRSA_Destroy + */ +static void +pem_mdMechanismRSA_Destroy +( + NSSCKMDMechanism * mdMechanism, + NSSCKFWMechanism * fwMechanism, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance +) +{ + nss_ZFreeIf(fwMechanism); +} + +/* + * pem_mdMechanismRSA_GetMinKeySize + */ +static CK_ULONG +pem_mdMechanismRSA_GetMinKeySize +( + NSSCKMDMechanism * mdMechanism, + NSSCKFWMechanism * fwMechanism, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_RV * pError +) +{ + return 384; +} + +/* + * pem_mdMechanismRSA_GetMaxKeySize + */ +static CK_ULONG +pem_mdMechanismRSA_GetMaxKeySize +( + NSSCKMDMechanism * mdMechanism, + NSSCKFWMechanism * fwMechanism, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_RV * pError +) +{ + return 16384; +} + +/* + * pem_mdMechanismRSA_DecryptInit + */ +static NSSCKMDCryptoOperation * +pem_mdMechanismRSA_DecryptInit +( + NSSCKMDMechanism * mdMechanism, + NSSCKFWMechanism * fwMechanism, + CK_MECHANISM * pMechanism, + NSSCKMDSession * mdSession, + NSSCKFWSession * fwSession, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + NSSCKMDObject * mdKey, + NSSCKFWObject * fwKey, + CK_RV * pError +) +{ + return pem_mdCryptoOperationRSAPriv_Create + (&pem_mdCryptoOperationRSADecrypt_proto, mdMechanism, mdKey, + pError); +} + +/* + * pem_mdMechanismRSA_SignInit + */ +static NSSCKMDCryptoOperation * +pem_mdMechanismRSA_SignInit +( + NSSCKMDMechanism * mdMechanism, + NSSCKFWMechanism * fwMechanism, + CK_MECHANISM * pMechanism, + NSSCKMDSession * mdSession, + NSSCKFWSession * fwSession, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + NSSCKMDObject * mdKey, + NSSCKFWObject * fwKey, + CK_RV * pError +) +{ + return pem_mdCryptoOperationRSAPriv_Create + (&pem_mdCryptoOperationRSASign_proto, mdMechanism, mdKey, pError); +} + +NSS_IMPLEMENT_DATA const NSSCKMDMechanism +pem_mdMechanismRSA = { + (void *) NULL, /* etc */ + pem_mdMechanismRSA_Destroy, + pem_mdMechanismRSA_GetMinKeySize, + pem_mdMechanismRSA_GetMaxKeySize, + NULL, /* GetInHardware - default false */ + NULL, /* EncryptInit - default errs */ + pem_mdMechanismRSA_DecryptInit, + NULL, /* DigestInit - default errs */ + pem_mdMechanismRSA_SignInit, + NULL, /* VerifyInit - default errs */ + pem_mdMechanismRSA_SignInit, /* SignRecoverInit */ + NULL, /* VerifyRecoverInit - default errs */ + NULL, /* GenerateKey - default errs */ + NULL, /* GenerateKeyPair - default errs */ + NULL, /* GetWrapKeyLength - default errs */ + NULL, /* WrapKey - default errs */ + NULL, /* UnwrapKey - default errs */ + NULL, /* DeriveKey - default errs */ + (void *) NULL /* null terminator */ +}; diff --git a/psession.c b/psession.c new file mode 100644 index 0000000..472e0b1 --- /dev/null +++ b/psession.c @@ -0,0 +1,452 @@ +/* ***** 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. + * + * Contributor(s): + * Rob Crittenden (rcritten@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 "ckpem.h" +#include "secmodt.h" +#include "pk11pub.h" +#include "base64.h" +#include "blapi.h" + +/* + * psession.c + * + * This file implements the NSSCKMDSession object for the + * "PEM objects" cryptoki module. + */ + +NSS_EXTERN_DATA pemInternalObject **gobj; +NSS_EXTERN_DATA int pem_nobjs; +NSS_EXTERN_DATA int token_needsLogin[NUM_SLOTS]; +NSS_EXTERN_DATA const SEC_ASN1Template pem_RSAPrivateKeyTemplate[]; + +void prepare_low_rsa_priv_key_for_asn1(NSSLOWKEYPrivateKey * key); +void pem_DestroyPrivateKey(NSSLOWKEYPrivateKey * privk); + +/* + * Convert a hex string into bytes. + */ +static unsigned char *convert_iv(char *src, int num) +{ + int i; + char conv[3]; + unsigned char *c; + + c = (unsigned char *) malloc((num) + 1); + if (c == NULL) + return NULL; + + conv[2] = '\0'; + memset(c, 0, num); + for (i = 0; i < num; i++) { + conv[0] = src[(i * 2)]; + conv[1] = src[(i * 2) + 1]; + c[i] = strtol(conv, NULL, 16); + } + return c; +} + +/* + * The key is a 24-bit hash. The first 16 bits are the MD5 hash of the + * password and the IV (salt). This has is then re-hashed with the + * password and IV again and the first 8 bytes of that are the remaining + * bytes of the 24-bit key. + */ +static int +make_key(const unsigned char *salt, const unsigned char *data, int len, + unsigned char *key) +{ + int nkey = 0; + MD5Context *Md5Ctx = MD5_NewContext(); + unsigned int digestLen; + int count, i; + char H[25]; + + nkey = 24; + count = 0; + + while (nkey > 0) { + MD5_Begin(Md5Ctx); + if (count) + MD5_Update(Md5Ctx, (const unsigned char *) H, digestLen); + MD5_Update(Md5Ctx, (const unsigned char *) data, len); + MD5_Update(Md5Ctx, (const unsigned char *) salt, 8); + MD5_End(Md5Ctx, (unsigned char *) H, &digestLen, sizeof(H)); + + i = 0; + while (nkey && (i != digestLen)) { + *(key++) = H[i]; + nkey--; + i++; + } + count++; + } + + return 24; +} + +static NSSCKMDFindObjects * +pem_mdSession_FindObjectsInit +( + NSSCKMDSession * mdSession, + NSSCKFWSession * fwSession, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_RV * pError +) +{ + plog("mdSession_FindObjectsInit\n"); + return pem_FindObjectsInit(fwSession, pTemplate, ulAttributeCount, + pError); +} + +static NSSCKMDObject * +pem_mdSession_CreateObject +( + NSSCKMDSession * mdSession, + NSSCKFWSession * fwSession, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + NSSArena * arena, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_RV * pError +) +{ + plog("mdSession_CreateObject\n"); + return pem_CreateObject(fwInstance, fwSession, mdToken, pTemplate, + ulAttributeCount, pError); +} + +NSSCKMDObject * +pem_mdSession_CopyObject +( + NSSCKMDSession * mdSession, + NSSCKFWSession * fwSession, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + NSSCKFWObject * mdOldObject, + NSSCKFWObject * fwOldObject, + NSSArena * arena, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_RV * pError +) +{ + CK_OBJECT_CLASS objClass; + pemInternalObject *new; + NSSCKMDObject *mdObject = nssCKFWObject_GetMDObject(fwOldObject); + pemInternalObject *old = (pemInternalObject *) mdObject->etc; + CK_RV error = CKR_OK; + + plog("pem_mdSession_CopyObject: "); + /* + * now handle the various object types + */ + *pError = CKR_OK; + switch (old->type) { + case pemCert: + plog("pemCert\n"); + if ((pemInternalObject *) NULL == 0) { + new = nss_ZNEW(NULL, pemInternalObject); + if ((pemInternalObject *) NULL == new) { + *pError = CKR_HOST_MEMORY; + goto loser; + } + memset(&new->u.cert, 0, sizeof(new->u.cert)); + new->objClass = objClass; + new->type = pemCert; + new->derCert = nss_ZNEW(NULL, SECItem); + new->derCert->data = + (void *) nss_ZAlloc(NULL, old->derCert->len); + new->derCert->len = old->derCert->len; + nsslibc_memcpy(new->derCert->data, old->derCert->data, + old->derCert->len); + new->nickname = + (char *) nss_ZAlloc(NULL, strlen(old->nickname) + 1); + strcpy(new->nickname, old->nickname); + new->id.data = (void *) nss_ZAlloc(NULL, old->id.size); + (void) nsslibc_memcpy(new->id.data, old->id.data, + old->id.size); + new->id.size = old->id.size; + } + break; + case pemBareKey: + plog("pemBareKey\n"); + if ((pemInternalObject *) NULL == 0) { + new = nss_ZNEW(NULL, pemInternalObject); + if ((pemInternalObject *) NULL == new) { + *pError = CKR_HOST_MEMORY; + goto loser; + } + memset(&new->u.key, 0, sizeof(new->u.key)); + new->objClass = CKO_PRIVATE_KEY; + new->type = pemBareKey; + new->derCert = nss_ZNEW(NULL, SECItem); + new->derCert->data = + (void *) nss_ZAlloc(NULL, old->derCert->len); + new->derCert->len = old->derCert->len; + new->id.data = (void *) nss_ZAlloc(NULL, old->id.size); + (void) nsslibc_memcpy(new->id.data, old->id.data, + old->id.size); + new->id.size = old->id.size; + nsslibc_memcpy(new->derCert->data, old->derCert->data, + old->derCert->len); + new->nickname = + (char *) nss_ZAlloc(NULL, strlen(old->nickname) + 1); + strcpy(new->nickname, old->nickname); + new->u.key.key.privateKey = nss_ZNEW(NULL, SECItem); + new->u.key.key.privateKey->data = + (void *) nss_ZAlloc(NULL, old->u.key.key.privateKey->len); + new->u.key.key.privateKey->len = + old->u.key.key.privateKey->len; + nsslibc_memcpy(new->u.key.key.privateKey->data, + old->u.key.key.privateKey->data, + old->u.key.key.privateKey->len); + } + goto done; + break; + case pemTrust: + plog("pemTrust\n"); + if ((pemInternalObject *) NULL == 0) { + new = nss_ZNEW(NULL, pemInternalObject); + if ((pemInternalObject *) NULL == new) { + *pError = CKR_HOST_MEMORY; + goto loser; + } + memset(&new->u.trust, 0, sizeof(new->u.trust)); + new->objClass = objClass; + new->type = pemTrust; + new->derCert = nss_ZNEW(NULL, SECItem); + new->derCert->data = + (void *) nss_ZAlloc(NULL, old->derCert->len); + new->derCert->len = old->derCert->len; + nsslibc_memcpy(new->derCert->data, old->derCert->data, + old->derCert->len); + new->nickname = + (char *) nss_ZAlloc(NULL, strlen(old->nickname) + 1); + strcpy(new->nickname, old->nickname); + new->id.data = (void *) nss_ZAlloc(NULL, old->id.size); + (void) nsslibc_memcpy(new->id.data, old->id.data, + old->id.size); + new->id.size = old->id.size; + } + goto done; + default: + plog("Unknown: %08x\n", old->type); + goto done; /* no other object types we understand in this module */ + } + if (CKR_OK != *pError) { + goto loser; + } + + done: + *pError = CKR_OK; + return (NSSCKMDObject *) pem_CreateMDObject(arena, new, &error); + + loser: + *pError = CKR_GENERAL_ERROR; + return (NSSCKMDObject *) NULL; + +} + +CK_RV +pem_mdSession_Login +( + NSSCKMDSession * mdSession, + NSSCKFWSession * fwSession, + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_USER_TYPE userType, + NSSItem * pin, + CK_STATE oldState, + CK_STATE newState +) +{ + NSSCKFWSlot *fwSlot; + CK_SLOT_ID slotID; + pemInternalObject *io; + unsigned char *iv; + unsigned char mykey[32]; + unsigned char *output; + DESContext *cx = NULL; + SECStatus rv; + unsigned int len = 0; + NSSLOWKEYPrivateKey *lpk = NULL; + PLArenaPool *arena; + int i; + + fwSlot = NSSCKFWToken_GetFWSlot(fwToken); + slotID = nssCKFWSlot_GetSlotID(fwSlot); + + arena = PORT_NewArena(2048); + if (!arena) { + goto loser; + } + + plog("pem_mdSession_Login '%s'\n", (char *) pin->data); + + token_needsLogin[slotID - 1] = PR_FALSE; + + /* Find the right key object */ + for (i = 0; i < pem_nobjs; i++) { + if ((slotID == gobj[i]->slotID) && (gobj[i]->type == pemBareKey)) { + io = gobj[i]; + break; + } + } + + /* Convert the IV from hex into an array of bytes */ + iv = convert_iv(io->u.key.ivstring, 8); + + /* Convert the PIN and IV into a DES key */ + make_key(iv, pin->data, 8, mykey); + + output = + (unsigned char *) nss_ZAlloc(NULL, + (io->u.key.key.privateKey->len + 1)); + + cx = DES_CreateContext((const unsigned char *) mykey, iv, + io->u.key.cipher, PR_FALSE); + + rv = DES_Decrypt(cx, output, &len, io->u.key.key.privateKey->len, + io->u.key.key.privateKey->data, + io->u.key.key.privateKey->len); + DES_DestroyContext(cx, PR_TRUE); + + if (iv) { + free(iv); + iv = NULL; + } + if (rv != SECSuccess) { + goto loser; + } + + lpk = (NSSLOWKEYPrivateKey *) nss_ZAlloc(NULL, + sizeof (NSSLOWKEYPrivateKey)); + if (lpk == NULL) { + goto loser; + } + + lpk->arena = arena; + lpk->keyType = NSSLOWKEYRSAKey; + prepare_low_rsa_priv_key_for_asn1(lpk); + + nss_ZFreeIf(io->u.key.key.privateKey->data); + io->u.key.key.privateKey->len = len - output[len - 1]; + io->u.key.key.privateKey->data = + (void *) nss_ZAlloc(NULL, io->u.key.key.privateKey->len); + memcpy(io->u.key.key.privateKey->data, output, len - output[len - 1]); + + /* Decode the resulting blob and see if it is a decodable DER that fits + * our private key template. If so we declare success and move on. If not + * then we return an error. + */ + rv = SEC_QuickDERDecodeItem(arena, lpk, pem_RSAPrivateKeyTemplate, + io->u.key.key.privateKey); + pem_DestroyPrivateKey(lpk); + arena = NULL; + if (rv != SECSuccess) + goto loser; + + return CKR_OK; + + loser: + if (arena) + PORT_FreeArena(arena, PR_FALSE); + if (iv) + free(iv); + nss_ZFreeIf(output); + + return CKR_PIN_INCORRECT; +} + +NSS_IMPLEMENT NSSCKMDSession * +pem_CreateSession +( + NSSCKFWSession * fwSession, + CK_RV * pError +) +{ + NSSArena *arena; + NSSCKMDSession *rv; + + plog("pem_CreateSession returning new session\n"); + arena = NSSCKFWSession_GetArena(fwSession, pError); + if ((NSSArena *) NULL == arena) { + return (NSSCKMDSession *) NULL; + } + + rv = nss_ZNEW(arena, NSSCKMDSession); + if ((NSSCKMDSession *) NULL == rv) { + *pError = CKR_HOST_MEMORY; + return (NSSCKMDSession *) NULL; + } + + /* + * rv was zeroed when allocated, so we only + * need to set the non-zero members. + */ + + rv->etc = (void *) fwSession; + /* rv->Close */ + /* rv->GetDeviceError */ + rv->Login = pem_mdSession_Login; + /* rv->Logout */ + /* rv->InitPIN */ + /* rv->SetPIN */ + /* rv->GetOperationStateLen */ + /* rv->GetOperationState */ + /* rv->SetOperationState */ + rv->CreateObject = pem_mdSession_CreateObject; + rv->CopyObject = pem_mdSession_CopyObject; + rv->FindObjectsInit = pem_mdSession_FindObjectsInit; + /* rv->SeedRandom */ + /* rv->GetRandom */ + /* rv->null */ + + return rv; +} diff --git a/pslot.c b/pslot.c new file mode 100644 index 0000000..587e237 --- /dev/null +++ b/pslot.c @@ -0,0 +1,184 @@ +/* ***** 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. + * + * Contributor(s): + * Rob Crittenden (rcritten@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 "ckpem.h" + +/* + * pslot.c + * + * This file implements the NSSCKMDSlot object for the + * "PEM objects" cryptoki module. + */ + +static NSSUTF8 * +pem_mdSlot_GetSlotDescription +( + NSSCKMDSlot * mdSlot, + NSSCKFWSlot * fwSlot, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_RV * pError +) +{ + CK_SLOT_ID slotID; + NSSArena *arena; + char *slotid; + + arena = NSSCKFWInstance_GetArena(fwInstance, pError); + slotID = nssCKFWSlot_GetSlotID(fwSlot); + + slotid = (char *) nss_ZAlloc(arena, 256); + snprintf(slotid, 256, "PEM Slot #%ld", slotID); + + return (NSSUTF8 *) slotid; +} + +static NSSUTF8 * +pem_mdSlot_GetManufacturerID +( + NSSCKMDSlot * mdSlot, + NSSCKFWSlot * fwSlot, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_RV * pError +) +{ + return (NSSUTF8 *) pem_ManufacturerID; +} + +static CK_VERSION +pem_mdSlot_GetHardwareVersion +( + NSSCKMDSlot * mdSlot, + NSSCKFWSlot * fwSlot, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance +) +{ + return pem_HardwareVersion; +} + +static CK_VERSION +pem_mdSlot_GetFirmwareVersion +( + NSSCKMDSlot * mdSlot, + NSSCKFWSlot * fwSlot, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance +) +{ + return pem_FirmwareVersion; +} + +static NSSCKMDToken * +pem_mdSlot_GetToken +( + NSSCKMDSlot * mdSlot, + NSSCKFWSlot * fwSlot, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_RV * pError +) +{ + return (NSSCKMDToken *) mdSlot->etc; +} + +CK_BBOOL +pem_mdSlot_GetRemovableDevice +( + NSSCKMDSlot * mdSlot, + NSSCKFWSlot * fwSlot, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance +) +{ + return CK_TRUE; +} + +NSSCKMDSlot * +pem_NewSlot +( + NSSCKFWInstance * fwInstance, + CK_RV * pError +) +{ + NSSArena *arena; + NSSCKMDSlot *mdSlot; + pemToken *token = NULL; + + plog("pem_NewSlot\n"); + arena = NSSCKFWInstance_GetArena(fwInstance, pError); + if ((NSSArena *) NULL == arena) { + if (CKR_OK == *pError) { + *pError = CKR_GENERAL_ERROR; + } + } + + mdSlot = nss_ZNEW(arena, NSSCKMDSlot); + if ((NSSCKMDSlot *) NULL == mdSlot) { + *pError = CKR_HOST_MEMORY; + return (NSSCKMDSlot *) NULL; + } + + token = pem_NewToken(fwInstance, pError); + + mdSlot->etc = token; + mdSlot->GetSlotDescription = pem_mdSlot_GetSlotDescription; + mdSlot->GetManufacturerID = pem_mdSlot_GetManufacturerID; + mdSlot->GetHardwareVersion = pem_mdSlot_GetHardwareVersion; + mdSlot->GetFirmwareVersion = pem_mdSlot_GetFirmwareVersion; + mdSlot->GetRemovableDevice = pem_mdSlot_GetRemovableDevice; + mdSlot->GetToken = pem_mdSlot_GetToken; + + return mdSlot; +} + +NSS_IMPLEMENT_DATA const NSSCKMDSlot +pem_mdSlot = { + (void *) NULL, /* etc */ + NULL, /* Initialize */ + NULL, /* Destroy */ + pem_mdSlot_GetSlotDescription, + pem_mdSlot_GetManufacturerID, + NULL, /* GetTokenPresent -- defaults to true */ + pem_mdSlot_GetRemovableDevice, + NULL, /* GetHardwareSlot -- defaults to false */ + pem_mdSlot_GetHardwareVersion, + pem_mdSlot_GetFirmwareVersion, + pem_mdSlot_GetToken, + (void *) NULL /* null terminator */ +}; diff --git a/ptoken.c b/ptoken.c new file mode 100644 index 0000000..67b5f10 --- /dev/null +++ b/ptoken.c @@ -0,0 +1,333 @@ +/* ***** 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. + * + * Contributor(s): + * Rob Crittenden (rcritten@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 "ckpem.h" + +/* + * ptoken.c + * + * This file implements the NSSCKMDToken object for the + * "PEM objects" cryptoki module. + */ + +NSS_EXTERN_DATA int token_needsLogin[NUM_SLOTS]; + +static NSSUTF8 * +pem_mdToken_GetLabel +( + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_RV * pError +) +{ + NSSCKFWSlot *fwSlot; + CK_SLOT_ID slotID; + NSSArena *arena; + char *tokenid; + + arena = NSSCKFWInstance_GetArena(fwInstance, pError); + fwSlot = NSSCKFWToken_GetFWSlot(fwToken); + slotID = nssCKFWSlot_GetSlotID(fwSlot); + + tokenid = (char *) nss_ZAlloc(arena, 256); + snprintf(tokenid, 256, "PEM Token #%ld", slotID); + + return (NSSUTF8 *) tokenid; +} + +static NSSUTF8 * +pem_mdToken_GetManufacturerID +( + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_RV * pError +) +{ + return (NSSUTF8 *) pem_ManufacturerID; +} + +static NSSUTF8 * +pem_mdToken_GetModel +( + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_RV * pError +) +{ + return (NSSUTF8 *) pem_TokenModel; +} + +static NSSUTF8 * +pem_mdToken_GetSerialNumber +( + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_RV * pError +) +{ + return (NSSUTF8 *) pem_TokenSerialNumber; +} + +static CK_BBOOL +pem_mdToken_GetIsWriteProtected +( + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance +) +{ + return CK_TRUE; +} + +static CK_VERSION +pem_mdToken_GetHardwareVersion +( + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance +) +{ + return pem_HardwareVersion; +} + +static CK_VERSION +pem_mdToken_GetFirmwareVersion +( + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance +) +{ + return pem_FirmwareVersion; +} + +static NSSCKMDSession *pem_mdToken_OpenSession +( + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + NSSCKFWSession * fwSession, + CK_BBOOL rw, + CK_RV * pError +) +{ + plog("pem_mdToken_OpenSession\n"); + return pem_CreateSession(fwSession, pError); +} + +static CK_ULONG +pem_mdToken_GetMechanismCount +( + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance +) +{ + return (CK_ULONG) 1; +} + +static CK_RV +pem_mdToken_GetMechanismTypes +( + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_MECHANISM_TYPE types[] +) +{ + types[0] = CKM_RSA_PKCS; + return CKR_OK; +} + +static NSSCKMDMechanism * +pem_mdToken_GetMechanism +( + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance, + CK_MECHANISM_TYPE which, + CK_RV * pError +) +{ + if (which != CKM_RSA_PKCS) { + *pError = CKR_MECHANISM_INVALID; + return (NSSCKMDMechanism *) NULL; + } + return (NSSCKMDMechanism *) & pem_mdMechanismRSA; +} + +static CK_BBOOL +pem_mdToken_GetUserPinInitialized +( + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance +) +{ + plog("pem_mdToken_GetUserPinInitialized: always TRUE\n"); + return CK_TRUE; +} + +static CK_BBOOL +pem_mdToken_GetLoginRequired +( + NSSCKMDToken * mdToken, + NSSCKFWToken * fwToken, + NSSCKMDInstance * mdInstance, + NSSCKFWInstance * fwInstance +) +{ + char *label; + CK_RV pError; + NSSCKFWSlot *fwSlot; + CK_SLOT_ID slotID; + + fwSlot = NSSCKFWToken_GetFWSlot(fwToken); + slotID = nssCKFWSlot_GetSlotID(fwSlot); + + label = pem_mdToken_GetLabel(mdToken, fwToken, mdInstance, fwInstance, + &pError); + + plog("pem_mdToken_GetLoginRequired %s: %d\n", label, + token_needsLogin[slotID - 1]); + + if (token_needsLogin[slotID - 1] == PR_TRUE) + return CK_TRUE; + else + return CK_FALSE; +} + +NSSCKMDToken * +pem_NewToken +( + NSSCKFWInstance * fwInstance, + CK_RV * pError +) +{ + NSSArena *arena; + NSSCKMDToken *mdToken; + pemToken *token; + + arena = NSSCKFWInstance_GetArena(fwInstance, pError); + if ((NSSArena *) NULL == arena) { + if (CKR_OK == *pError) { + *pError = CKR_GENERAL_ERROR; + } + } + + mdToken = nss_ZNEW(arena, NSSCKMDToken); + if ((NSSCKMDToken *) NULL == mdToken) { + *pError = CKR_HOST_MEMORY; + return (NSSCKMDToken *) NULL; + } + + token = nss_ZNEW(arena, struct pemTokenStr); + if ((struct pemTokenStr *) NULL == token) { + *pError = CKR_HOST_MEMORY; + return (NSSCKMDToken *) NULL; + } + + mdToken->etc = (void *) token; + mdToken->GetLabel = pem_mdToken_GetLabel; + mdToken->GetManufacturerID = pem_mdToken_GetManufacturerID; + mdToken->GetModel = pem_mdToken_GetModel; + mdToken->GetSerialNumber = pem_mdToken_GetSerialNumber; + mdToken->GetIsWriteProtected = pem_mdToken_GetIsWriteProtected; + mdToken->GetLoginRequired = pem_mdToken_GetLoginRequired; + mdToken->GetUserPinInitialized = pem_mdToken_GetUserPinInitialized; + mdToken->GetHardwareVersion = pem_mdToken_GetHardwareVersion; + mdToken->GetFirmwareVersion = pem_mdToken_GetFirmwareVersion; + mdToken->OpenSession = pem_mdToken_OpenSession; + mdToken->GetMechanismCount = pem_mdToken_GetMechanismCount; + mdToken->GetMechanismTypes = pem_mdToken_GetMechanismTypes; + mdToken->GetMechanism = pem_mdToken_GetMechanism; + + return mdToken; +} + +#if 0 +NSS_IMPLEMENT_DATA const NSSCKMDToken pem_mdToken = { + (void *) NULL, /* etc */ + NULL, /* Setup */ + NULL, /* Invalidate */ + NULL, /* InitToken -- default errs */ + pem_mdToken_GetLabel, + pem_mdToken_GetManufacturerID, + pem_mdToken_GetModel, + pem_mdToken_GetSerialNumber, + NULL, /* GetHasRNG -- default is false */ + pem_mdToken_GetIsWriteProtected, + pem_mdToken_GetLoginRequired, + pem_mdToken_GetUserPinInitialized, + NULL, /* GetRestoreKeyNotNeeded -- irrelevant */ + NULL, /* GetHasClockOnToken -- default is false */ + NULL, /* GetHasProtectedAuthenticationPath -- default is false */ + NULL, /* GetSupportsDualCryptoOperations -- default is false */ + NULL, /* GetMaxSessionCount -- default is CK_UNAVAILABLE_INFORMATION */ + NULL, /* GetMaxRwSessionCount -- default is CK_UNAVAILABLE_INFORMATION */ + NULL, /* GetMaxPinLen -- irrelevant */ + NULL, /* GetMinPinLen -- irrelevant */ + NULL, /* GetTotalPublicMemory -- default is CK_UNAVAILABLE_INFORMATION */ + NULL, /* GetFreePublicMemory -- default is CK_UNAVAILABLE_INFORMATION */ + NULL, /* GetTotalPrivateMemory -- default is CK_UNAVAILABLE_INFORMATION */ + NULL, /* GetFreePrivateMemory -- default is CK_UNAVAILABLE_INFORMATION */ + pem_mdToken_GetHardwareVersion, + pem_mdToken_GetFirmwareVersion, + NULL, /* GetUTCTime -- no clock */ + pem_mdToken_OpenSession, + pem_mdToken_GetMechanismCount, + pem_mdToken_GetMechanismTypes, + pem_mdToken_GetMechanism, + (void *) NULL /* null terminator */ +}; +#endif diff --git a/rsawrapr.c b/rsawrapr.c new file mode 100644 index 0000000..5ac4f39 --- /dev/null +++ b/rsawrapr.c @@ -0,0 +1,924 @@ +/* + * PKCS#1 encoding and decoding functions. + * This file is believed to contain no code licensed from other parties. + * + * ***** 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. + * + * Contributor(s): + * Rob Crittenden (rcritten@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 ***** */ +/* $Id: $ */ + +#include "ckpem.h" +#include "blapi.h" +#include "softoken.h" +#include "sechash.h" +#include "base.h" + +#include "secerr.h" + +#define RSA_BLOCK_MIN_PAD_LEN 8 +#define RSA_BLOCK_FIRST_OCTET 0x00 +#define RSA_BLOCK_PRIVATE0_PAD_OCTET 0x00 +#define RSA_BLOCK_PRIVATE_PAD_OCTET 0xff +#define RSA_BLOCK_AFTER_PAD_OCTET 0x00 + +#define OAEP_SALT_LEN 8 +#define OAEP_PAD_LEN 8 +#define OAEP_PAD_OCTET 0x00 + +#define FLAT_BUFSIZE 512 /* bytes to hold flattened SHA1Context. */ + +unsigned +pem_PublicModulusLen(NSSLOWKEYPublicKey *pubk) +{ + unsigned char b0; + + /* interpret modulus length as key strength... in + * fortezza that's the public key length */ + + switch (pubk->keyType) { + case pemLOWKEYRSAKey: + b0 = pubk->u.rsa.modulus.data[0]; + return b0 ? pubk->u.rsa.modulus.len : pubk->u.rsa.modulus.len - 1; + default: + break; + } + return 0; +} + +static SHA1Context *SHA1_CloneContext(SHA1Context * original) +{ + SHA1Context *clone = NULL; + unsigned char *pBuf; + int sha1ContextSize = SHA1_FlattenSize(original); + SECStatus frv; + unsigned char buf[FLAT_BUFSIZE]; + + PORT_Assert(sizeof buf >= sha1ContextSize); + if (sizeof buf >= sha1ContextSize) { + pBuf = buf; + } else { + pBuf = nss_ZAlloc(NULL, sha1ContextSize); + if (!pBuf) + goto done; + } + + frv = SHA1_Flatten(original, pBuf); + if (frv == SECSuccess) { + clone = SHA1_Resurrect(pBuf, NULL); + memset(pBuf, 0, sha1ContextSize); + } + done: + if (pBuf != buf) + nss_ZFreeIf(pBuf); + return clone; +} + +/* + * Modify data by XORing it with a special hash of salt. + */ +static SECStatus +oaep_xor_with_h1(unsigned char *data, unsigned int datalen, + unsigned char *salt, unsigned int saltlen) +{ + SHA1Context *sha1cx; + unsigned char *dp, *dataend; + unsigned char end_octet; + + sha1cx = SHA1_NewContext(); + if (sha1cx == NULL) { + return SECFailure; + } + + /* + * Get a hash of salt started; we will use it several times, + * adding in a different end octet (x00, x01, x02, ...). + */ + SHA1_Begin(sha1cx); + SHA1_Update(sha1cx, salt, saltlen); + end_octet = 0; + + dp = data; + dataend = data + datalen; + + while (dp < dataend) { + SHA1Context *sha1cx_h1; + unsigned int sha1len, sha1off; + unsigned char sha1[SHA1_LENGTH]; + + /* + * Create hash of (salt || end_octet) + */ + sha1cx_h1 = SHA1_CloneContext(sha1cx); + SHA1_Update(sha1cx_h1, &end_octet, 1); + SHA1_End(sha1cx_h1, sha1, &sha1len, sizeof(sha1)); + SHA1_DestroyContext(sha1cx_h1, PR_TRUE); + PORT_Assert(sha1len == SHA1_LENGTH); + + /* + * XOR that hash with the data. + * When we have fewer than SHA1_LENGTH octets of data + * left to xor, use just the low-order ones of the hash. + */ + sha1off = 0; + if ((dataend - dp) < SHA1_LENGTH) + sha1off = SHA1_LENGTH - (dataend - dp); + while (sha1off < SHA1_LENGTH) + *dp++ ^= sha1[sha1off++]; + + /* + * Bump for next hash chunk. + */ + end_octet++; + } + + SHA1_DestroyContext(sha1cx, PR_TRUE); + return SECSuccess; +} + +/* + * Modify salt by XORing it with a special hash of data. + */ +static SECStatus +oaep_xor_with_h2(unsigned char *salt, unsigned int saltlen, + unsigned char *data, unsigned int datalen) +{ + unsigned char sha1[SHA1_LENGTH]; + unsigned char *psalt, *psha1, *saltend; + SECStatus rv; + + /* + * Create a hash of data. + */ + rv = SHA1_HashBuf(sha1, data, datalen); + if (rv != SECSuccess) { + return rv; + } + + /* + * XOR the low-order octets of that hash with salt. + */ + PORT_Assert(saltlen <= SHA1_LENGTH); + saltend = salt + saltlen; + psalt = salt; + psha1 = sha1 + SHA1_LENGTH - saltlen; + while (psalt < saltend) { + *psalt++ ^= *psha1++; + } + + return SECSuccess; +} + +/* + * Format one block of data for public/private key encryption using + * the rules defined in PKCS #1. + */ +static unsigned char *rsa_FormatOneBlock(unsigned modulusLen, + RSA_BlockType blockType, + SECItem * data) +{ + unsigned char *block; + unsigned char *bp; + int padLen; + int i; + SECStatus rv; + + block = (unsigned char *) nss_ZAlloc(NULL, modulusLen); + if (block == NULL) + return NULL; + + bp = block; + + /* + * All RSA blocks start with two octets: + * 0x00 || BlockType + */ + *bp++ = RSA_BLOCK_FIRST_OCTET; + *bp++ = (unsigned char) blockType; + + switch (blockType) { + + /* + * Blocks intended for private-key operation. + */ + case RSA_BlockPrivate0: /* essentially unused */ + case RSA_BlockPrivate: /* preferred method */ + /* + * 0x00 || BT || Pad || 0x00 || ActualData + * 1 1 padLen 1 data->len + * Pad is either all 0x00 or all 0xff bytes, depending on blockType. + */ + padLen = modulusLen - data->len - 3; + PORT_Assert(padLen >= RSA_BLOCK_MIN_PAD_LEN); + if (padLen < RSA_BLOCK_MIN_PAD_LEN) { + nss_ZFreeIf(block); + return NULL; + } + nsslibc_memset(bp, + blockType == RSA_BlockPrivate0 + ? RSA_BLOCK_PRIVATE0_PAD_OCTET + : RSA_BLOCK_PRIVATE_PAD_OCTET, padLen); + bp += padLen; + *bp++ = RSA_BLOCK_AFTER_PAD_OCTET; + nsslibc_memcpy(bp, data->data, data->len); + break; + + /* + * Blocks intended for public-key operation. + */ + case RSA_BlockPublic: + + /* + * 0x00 || BT || Pad || 0x00 || ActualData + * 1 1 padLen 1 data->len + * Pad is all non-zero random bytes. + */ + padLen = modulusLen - data->len - 3; + PORT_Assert(padLen >= RSA_BLOCK_MIN_PAD_LEN); + if (padLen < RSA_BLOCK_MIN_PAD_LEN) { + nss_ZFreeIf(block); + return NULL; + } + for (i = 0; i < padLen; i++) { + /* Pad with non-zero random data. */ + do { + rv = RNG_GenerateGlobalRandomBytes(bp + i, 1); + } while (rv == SECSuccess + && bp[i] == RSA_BLOCK_AFTER_PAD_OCTET); + if (rv != SECSuccess) { + nss_ZFreeIf(block); + return NULL; + } + } + bp += padLen; + *bp++ = RSA_BLOCK_AFTER_PAD_OCTET; + nsslibc_memcpy(bp, data->data, data->len); + + break; + + /* + * Blocks intended for public-key operation, using + * Optimal Asymmetric Encryption Padding (OAEP). + */ + case RSA_BlockOAEP: + /* + * 0x00 || BT || Modified2(Salt) || Modified1(PaddedData) + * 1 1 OAEP_SALT_LEN OAEP_PAD_LEN + data->len [+ N] + * + * where: + * PaddedData is "Pad1 || ActualData [|| Pad2]" + * Salt is random data. + * Pad1 is all zeros. + * Pad2, if present, is random data. + * (The "modified" fields are all the same length as the original + * unmodified values; they are just xor'd with other values.) + * + * Modified1 is an XOR of PaddedData with a special octet + * string constructed of iterated hashing of Salt (see below). + * Modified2 is an XOR of Salt with the low-order octets of + * the hash of Modified1 (see farther below ;-). + * + * Whew! + */ + + + /* + * Salt + */ + rv = RNG_GenerateGlobalRandomBytes(bp, OAEP_SALT_LEN); + if (rv != SECSuccess) { + nss_ZFreeIf(block); + return NULL; + } + bp += OAEP_SALT_LEN; + + /* + * Pad1 + */ + nsslibc_memset(bp, OAEP_PAD_OCTET, OAEP_PAD_LEN); + bp += OAEP_PAD_LEN; + + /* + * Data + */ + nsslibc_memcpy(bp, data->data, data->len); + bp += data->len; + + /* + * Pad2 + */ + if (bp < (block + modulusLen)) { + rv = RNG_GenerateGlobalRandomBytes(bp, + block - bp + modulusLen); + if (rv != SECSuccess) { + nss_ZFreeIf(block); + return NULL; + } + } + + /* + * Now we have the following: + * 0x00 || BT || Salt || PaddedData + * (From this point on, "Pad1 || Data [|| Pad2]" is treated + * as the one entity PaddedData.) + * + * We need to turn PaddedData into Modified1. + */ + if (oaep_xor_with_h1(block + 2 + OAEP_SALT_LEN, + modulusLen - 2 - OAEP_SALT_LEN, + block + 2, OAEP_SALT_LEN) != SECSuccess) { + nss_ZFreeIf(block); + return NULL; + } + + /* + * Now we have: + * 0x00 || BT || Salt || Modified1(PaddedData) + * + * The remaining task is to turn Salt into Modified2. + */ + if (oaep_xor_with_h2(block + 2, OAEP_SALT_LEN, + block + 2 + OAEP_SALT_LEN, + modulusLen - 2 - OAEP_SALT_LEN) != + SECSuccess) { + nss_ZFreeIf(block); + return NULL; + } + + break; + + default: + PORT_Assert(0); + nss_ZFreeIf(block); + return NULL; + } + + return block; +} + +static SECStatus +rsa_FormatBlock(SECItem * result, unsigned modulusLen, + RSA_BlockType blockType, SECItem * data) +{ + /* + * XXX For now assume that the data length fits in a single + * XXX encryption block; the ASSERTs below force this. + * XXX To fix it, each case will have to loop over chunks whose + * XXX lengths satisfy the assertions, until all data is handled. + * XXX (Unless RSA has more to say about how to handle data + * XXX which does not fit in a single encryption block?) + * XXX And I do not know what the result is supposed to be, + * XXX so the interface to this function may need to change + * XXX to allow for returning multiple blocks, if they are + * XXX not wanted simply concatenated one after the other. + */ + + switch (blockType) { + case RSA_BlockPrivate0: + case RSA_BlockPrivate: + case RSA_BlockPublic: + /* + * 0x00 || BT || Pad || 0x00 || ActualData + * + * The "3" below is the first octet + the second octet + the 0x00 + * octet that always comes just before the ActualData. + */ + PORT_Assert(data->len <= + (modulusLen - (3 + RSA_BLOCK_MIN_PAD_LEN))); + + result->data = rsa_FormatOneBlock(modulusLen, blockType, data); + if (result->data == NULL) { + result->len = 0; + return SECFailure; + } + result->len = modulusLen; + + break; + + case RSA_BlockOAEP: + /* + * 0x00 || BT || M1(Salt) || M2(Pad1||ActualData[||Pad2]) + * + * The "2" below is the first octet + the second octet. + * (The other fields do not contain the clear values, but are + * the same length as the clear values.) + */ + PORT_Assert(data->len <= (modulusLen - (2 + OAEP_SALT_LEN + + OAEP_PAD_LEN))); + + result->data = rsa_FormatOneBlock(modulusLen, blockType, data); + if (result->data == NULL) { + result->len = 0; + return SECFailure; + } + result->len = modulusLen; + + break; + + case RSA_BlockRaw: + /* + * Pad || ActualData + * Pad is zeros. The application is responsible for recovering + * the actual data. + */ + if (data->len > modulusLen) { + return SECFailure; + } + result->data = (unsigned char *) nss_ZAlloc(NULL, modulusLen); + result->len = modulusLen; + nsslibc_memcpy(result->data + (modulusLen - data->len), data->data, + data->len); + break; + + default: + PORT_Assert(0); + result->data = NULL; + result->len = 0; + return SECFailure; + } + + return SECSuccess; +} + +/* XXX Doesn't set error code */ +SECStatus +pem_RSA_Sign(pemLOWKEYPrivateKey * key, + unsigned char *output, + unsigned int *output_len, + unsigned int maxOutputLen, + unsigned char *input, unsigned int input_len) +{ + SECStatus rv = SECSuccess; + unsigned int modulus_len = pem_PrivateModulusLen(key); + SECItem formatted; + SECItem unformatted; + + if (maxOutputLen < modulus_len) + return SECFailure; + PORT_Assert(key->keyType == pemLOWKEYRSAKey); + if (key->keyType != pemLOWKEYRSAKey) + return SECFailure; + + unformatted.len = input_len; + unformatted.data = input; + formatted.data = NULL; + rv = rsa_FormatBlock(&formatted, modulus_len, RSA_BlockPrivate, + &unformatted); + if (rv != SECSuccess) + goto done; + + rv = RSA_PrivateKeyOpDoubleChecked(&key->u.rsa, output, + formatted.data); + *output_len = modulus_len; + + goto done; + + done: + if (formatted.data != NULL) + nss_ZFreeIf(formatted.data); + return rv; +} + +#if 0 +/* XXX Doesn't set error code */ +SECStatus +RSA_CheckSign(NSSLOWKEYPublicKey * key, + unsigned char *sign, + unsigned int sign_len, + unsigned char *hash, unsigned int hash_len) +{ + SECStatus rv; + unsigned int modulus_len = pem_PublicModulusLen(key); + unsigned int i; + unsigned char *buffer; + + modulus_len = pem_PublicModulusLen(key); + if (sign_len != modulus_len) + goto failure; + /* + * 0x00 || BT || Pad || 0x00 || ActualData + * + * The "3" below is the first octet + the second octet + the 0x00 + * octet that always comes just before the ActualData. + */ + if (hash_len > modulus_len - (3 + RSA_BLOCK_MIN_PAD_LEN)) + goto failure; + PORT_Assert(key->keyType == pemLOWKEYRSAKey); + if (key->keyType != pemLOWKEYRSAKey) + goto failure; + + buffer = (unsigned char *) nss_ZAlloc(NULL, modulus_len + 1); + if (!buffer) + goto failure; + + rv = RSA_PublicKeyOp(&key->u.rsa, buffer, sign); + if (rv != SECSuccess) + goto loser; + + /* + * check the padding that was used + */ + if (buffer[0] != 0 || buffer[1] != 1) + goto loser; + for (i = 2; i < modulus_len - hash_len - 1; i++) { + if (buffer[i] != 0xff) + goto loser; + } + if (buffer[i] != 0) + goto loser; + + /* + * make sure we get the same results + */ + if (memcmp(buffer + modulus_len - hash_len, hash, hash_len) != 0) + goto loser; + + nss_ZFreeIf(buffer); + return SECSuccess; + + loser: + nss_ZFreeIf(buffer); + failure: + return SECFailure; +} + +/* XXX Doesn't set error code */ +SECStatus +RSA_CheckSignRecover(NSSLOWKEYPublicKey * key, + unsigned char *data, + unsigned int *data_len, + unsigned int max_output_len, + unsigned char *sign, unsigned int sign_len) +{ + SECStatus rv; + unsigned int modulus_len = pem_PublicModulusLen(key); + unsigned int i; + unsigned char *buffer; + + if (sign_len != modulus_len) + goto failure; + PORT_Assert(key->keyType == pemLOWKEYRSAKey); + if (key->keyType != pemLOWKEYRSAKey) + goto failure; + + buffer = (unsigned char *) nss_ZAlloc(NULL, modulus_len + 1); + if (!buffer) + goto failure; + + rv = RSA_PublicKeyOp(&key->u.rsa, buffer, sign); + if (rv != SECSuccess) + goto loser; + *data_len = 0; + + /* + * check the padding that was used + */ + if (buffer[0] != 0 || buffer[1] != 1) + goto loser; + for (i = 2; i < modulus_len; i++) { + if (buffer[i] == 0) { + *data_len = modulus_len - i - 1; + break; + } + if (buffer[i] != 0xff) + goto loser; + } + if (*data_len == 0) + goto loser; + if (*data_len > max_output_len) + goto loser; + + /* + * make sure we get the same results + */ + nsslibc_memcpy(data, buffer + modulus_len - *data_len, *data_len); + + nss_ZFreeIf(buffer); + return SECSuccess; + + loser: + nss_ZFreeIf(buffer); + failure: + return SECFailure; +} + +/* XXX Doesn't set error code */ +SECStatus +RSA_EncryptBlock(NSSLOWKEYPublicKey * key, + unsigned char *output, + unsigned int *output_len, + unsigned int max_output_len, + unsigned char *input, unsigned int input_len) +{ + SECStatus rv; + unsigned int modulus_len = pem_PublicModulusLen(key); + SECItem formatted; + SECItem unformatted; + + formatted.data = NULL; + if (max_output_len < modulus_len) + goto failure; + PORT_Assert(key->keyType == pemLOWKEYRSAKey); + if (key->keyType != pemLOWKEYRSAKey) + goto failure; + + unformatted.len = input_len; + unformatted.data = input; + formatted.data = NULL; + rv = rsa_FormatBlock(&formatted, modulus_len, RSA_BlockPublic, + &unformatted); + if (rv != SECSuccess) + goto failure; + + rv = RSA_PublicKeyOp(&key->u.rsa, output, formatted.data); + if (rv != SECSuccess) + goto failure; + + nss_ZFreeIf(formatted.data); + *output_len = modulus_len; + return SECSuccess; + + failure: + if (formatted.data != NULL) + nss_ZFreeIf(formatted.data); + return SECFailure; +} +#endif + +/* XXX Doesn't set error code */ +SECStatus +pem_RSA_DecryptBlock(pemLOWKEYPrivateKey * key, + unsigned char *output, + unsigned int *output_len, + unsigned int max_output_len, + unsigned char *input, unsigned int input_len) +{ + SECStatus rv; + unsigned int modulus_len = pem_PrivateModulusLen(key); + unsigned int i; + unsigned char *buffer; + + PORT_Assert(key->keyType == pemLOWKEYRSAKey); + if (key->keyType != pemLOWKEYRSAKey) + goto failure; + if (input_len != modulus_len) + goto failure; + + buffer = (unsigned char *) nss_ZAlloc(NULL, modulus_len + 1); + if (!buffer) + goto failure; + + rv = RSA_PrivateKeyOp(&key->u.rsa, buffer, input); + if (rv != SECSuccess) { + goto loser; + } + + if (buffer[0] != 0 || buffer[1] != 2) + goto loser; + *output_len = 0; + for (i = 2; i < modulus_len; i++) { + if (buffer[i] == 0) { + *output_len = modulus_len - i - 1; + break; + } + } + if (*output_len == 0) + goto loser; + if (*output_len > max_output_len) + goto loser; + + nsslibc_memcpy(output, buffer + modulus_len - *output_len, *output_len); + + nss_ZFreeIf(buffer); + return SECSuccess; + + loser: + nss_ZFreeIf(buffer); + failure: + return SECFailure; +} + +#if 0 +/* XXX Doesn't set error code */ +/* + * added to make pkcs #11 happy + * RAW is RSA_X_509 + */ +SECStatus +pem_RSA_SignRaw(pemLOWKEYPrivateKey * key, + unsigned char *output, + unsigned int *output_len, + unsigned int maxOutputLen, + unsigned char *input, unsigned int input_len) +{ + SECStatus rv = SECSuccess; + unsigned int modulus_len = pem_PrivateModulusLen(key); + SECItem formatted; + SECItem unformatted; + + if (maxOutputLen < modulus_len) + return SECFailure; + PORT_Assert(key->keyType == pemLOWKEYRSAKey); + if (key->keyType != pemLOWKEYRSAKey) + return SECFailure; + + unformatted.len = input_len; + unformatted.data = input; + formatted.data = NULL; + rv = rsa_FormatBlock(&formatted, modulus_len, RSA_BlockRaw, + &unformatted); + if (rv != SECSuccess) + goto done; + + rv = RSA_PrivateKeyOpDoubleChecked(&key->u.rsa, output, + formatted.data); + *output_len = modulus_len; + + done: + if (formatted.data != NULL) + nss_ZFreeIf(formatted.data); + return rv; +} + +/* XXX Doesn't set error code */ +SECStatus +RSA_CheckSignRaw(NSSLOWKEYPublicKey * key, + unsigned char *sign, + unsigned int sign_len, + unsigned char *hash, unsigned int hash_len) +{ + SECStatus rv; + unsigned int modulus_len = pem_PublicModulusLen(key); + unsigned char *buffer; + + if (sign_len != modulus_len) + goto failure; + if (hash_len > modulus_len) + goto failure; + PORT_Assert(key->keyType == pemLOWKEYRSAKey); + if (key->keyType != pemLOWKEYRSAKey) + goto failure; + + buffer = (unsigned char *) nss_ZAlloc(NULL, modulus_len + 1); + if (!buffer) + goto failure; + + rv = RSA_PublicKeyOp(&key->u.rsa, buffer, sign); + if (rv != SECSuccess) + goto loser; + + /* + * make sure we get the same results + */ + /* NOTE: should we verify the leading zeros? */ + if (memcmp(buffer + (modulus_len - hash_len), hash, hash_len) != + 0) + goto loser; + + nss_ZFreeIf(buffer); + return SECSuccess; + + loser: + nss_ZFreeIf(buffer); + failure: + return SECFailure; +} + +/* XXX Doesn't set error code */ +SECStatus +RSA_CheckSignRecoverRaw(NSSLOWKEYPublicKey * key, + unsigned char *data, + unsigned int *data_len, + unsigned int max_output_len, + unsigned char *sign, unsigned int sign_len) +{ + SECStatus rv; + unsigned int modulus_len = pem_PublicModulusLen(key); + + if (sign_len != modulus_len) + goto failure; + if (max_output_len < modulus_len) + goto failure; + PORT_Assert(key->keyType == pemLOWKEYRSAKey); + if (key->keyType != pemLOWKEYRSAKey) + goto failure; + + rv = RSA_PublicKeyOp(&key->u.rsa, data, sign); + if (rv != SECSuccess) + goto failure; + + *data_len = modulus_len; + return SECSuccess; + + failure: + return SECFailure; +} + + +/* XXX Doesn't set error code */ +SECStatus +RSA_EncryptRaw(NSSLOWKEYPublicKey * key, + unsigned char *output, + unsigned int *output_len, + unsigned int max_output_len, + unsigned char *input, unsigned int input_len) +{ + SECStatus rv; + unsigned int modulus_len = pem_PublicModulusLen(key); + SECItem formatted; + SECItem unformatted; + + formatted.data = NULL; + if (max_output_len < modulus_len) + goto failure; + PORT_Assert(key->keyType == pemLOWKEYRSAKey); + if (key->keyType != pemLOWKEYRSAKey) + goto failure; + + unformatted.len = input_len; + unformatted.data = input; + formatted.data = NULL; + rv = rsa_FormatBlock(&formatted, modulus_len, RSA_BlockRaw, + &unformatted); + if (rv != SECSuccess) + goto failure; + + rv = RSA_PublicKeyOp(&key->u.rsa, output, formatted.data); + if (rv != SECSuccess) + goto failure; + + nss_ZFreeIf(formatted.data); + *output_len = modulus_len; + return SECSuccess; + + failure: + if (formatted.data != NULL) + nss_ZFreeIf(formatted.data); + return SECFailure; +} + +/* XXX Doesn't set error code */ +SECStatus +pem_RSA_DecryptRaw(pemLOWKEYPrivateKey * key, + unsigned char *output, + unsigned int *output_len, + unsigned int max_output_len, + unsigned char *input, unsigned int input_len) +{ + SECStatus rv; + unsigned int modulus_len = pem_PrivateModulusLen(key); + + if (modulus_len <= 0) + goto failure; + if (modulus_len > max_output_len) + goto failure; + PORT_Assert(key->keyType == pemLOWKEYRSAKey); + if (key->keyType != pemLOWKEYRSAKey) + goto failure; + if (input_len != modulus_len) + goto failure; + + rv = RSA_PrivateKeyOp(&key->u.rsa, output, input); + if (rv != SECSuccess) { + goto failure; + } + + *output_len = modulus_len; + return SECSuccess; + + failure: + return SECFailure; +} +#endif diff --git a/util.c b/util.c new file mode 100644 index 0000000..338b89d --- /dev/null +++ b/util.c @@ -0,0 +1,288 @@ +/* ***** 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. + * + * Contributor(s): + * Rob Crittenden (rcritten@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 ***** */ + +/* cribbed from secutil.c */ + +#include "prtypes.h" +#include "prtime.h" +#include "prlong.h" +#include "prerror.h" +#include "prprf.h" +#include "plgetopt.h" +#include "prenv.h" +#include "prnetdb.h" +#include "base.h" +#include "base64.h" + +#include "cryptohi.h" +#include "secpkcs7.h" +#include "secerr.h" +#include + +#define CHUNK_SIZE 512 +#define PUT_Object(obj,err) \ + { \ + if (count >= size) { \ + *derlist = *derlist ? \ + nss_ZREALLOCARRAY(*derlist, SECItem *, \ + (size+CHUNK_SIZE) ) : \ + nss_ZNEWARRAY(NULL, SECItem *, \ + (size+CHUNK_SIZE) ) ; \ + if ((SECItem **)NULL == *derlist) { \ + err = CKR_HOST_MEMORY; \ + goto loser; \ + } \ + size += CHUNK_SIZE; \ + } \ + (*derlist)[ count ] = (obj); \ + count++; \ + } + +/* Read certificates from a flat file */ + +static SECItem *AllocItem(SECItem * item, unsigned int len) +{ + SECItem *result = NULL; + + if (item == NULL) { + result = nss_ZAlloc(NULL, sizeof(SECItem)); + if (result == NULL) { + goto loser; + } + } else { + PORT_Assert(item->data == NULL); + result = item; + } + + result->len = len; + if (len) { + result->data = nss_ZAlloc(NULL, len); + } + + return (result); + + loser: + if (result != NULL) { + SECITEM_FreeItem(result, (item == NULL) ? PR_TRUE : PR_FALSE); + } + return (NULL); +} + +static SECStatus FileToItem(SECItem * dst, PRFileDesc * src) +{ + PRFileInfo info; + PRInt32 numBytes; + PRStatus prStatus; + + prStatus = PR_GetOpenFileInfo(src, &info); + + if (prStatus != PR_SUCCESS) { + return SECFailure; + } + + /* XXX workaround for 3.1, not all utils zero dst before sending */ + dst->data = 0; + if (!AllocItem(dst, info.size+1)) + goto loser; + + numBytes = PR_Read(src, dst->data, info.size); + if (numBytes != info.size) { + goto loser; + } + + return SECSuccess; + loser: + SECITEM_FreeItem(dst, PR_FALSE); + nss_ZFreeIf(dst); + return SECFailure; +} + +int +ReadDERFromFile(SECItem *** derlist, char *filename, PRBool ascii, + int *cipher, char **ivstring, PRBool certsonly) +{ + SECStatus rv; + PRFileDesc *inFile; + int count = 0, size = 0; + SECItem *der; + int error; + SECItem filedata; + char *c, *iv; + + inFile = PR_Open(filename, PR_RDONLY, 0); + if (!inFile) + return -1; + + if (ascii) { + /* First convert ascii to binary */ + char *asc, *body; + + /* Read in ascii data */ + rv = FileToItem(&filedata, inFile); + asc = (char *) filedata.data; + if (!asc) { + PR_Close(inFile); + return -1; + } + + /* check for headers and trailers and remove them */ + if ((body = strstr(asc, "-----BEGIN")) != NULL) { + int key = 0; + while ((asc) && ((body = strstr(asc, "-----BEGIN")) != NULL)) { + key = 0; + if (strncmp(body, "-----BEGIN RSA PRIVATE KEY", 25) == 0) { + key = 1; + c = body; + body = strchr(body, '\n'); + body++; + if (strncmp(body, "Proc-Type: 4,ENCRYPTED", 22) == 0) { + body = strchr(body, '\n'); + body++; + if (strncmp(body, "DEK-Info: ", 10) == 0) { + body += 10; + c = body; + body = strchr(body, ','); + if (body == NULL) + goto loser; + *body = '\0'; + if (!strcasecmp(c, "DES-EDE3-CBC")) + *cipher = NSS_DES_EDE3_CBC; + else if (!strcasecmp(c, "DES-CBC")) + *cipher = NSS_DES_CBC; + else { + *cipher = -1; + goto loser; + } + body++; + iv = body; + body = strchr(body, '\n'); + if (body == NULL) + goto loser; + *body = '\0'; + body++; + *ivstring = strdup(iv); + } + } else { /* Else the private key is not encrypted */ + *cipher = 0; + body = c; + } + } + der = (SECItem *) malloc(sizeof(SECItem)); + + char *trailer = NULL; + asc = body; + body = strchr(body, '\n'); + if (!body) + body = strchr(asc, '\r'); /* maybe this is a MAC file */ + if (body) { + trailer = strstr(++body, "-----END"); + } + if (trailer != NULL) { + asc = trailer + 1; + *trailer = '\0'; + } else { + goto loser; + } + + /* Convert to binary */ + rv = ATOB_ConvertAsciiToItem(der, body); + if (rv) { + goto loser; + } + if ((certsonly && !key) || (!certsonly && key)) + PUT_Object(der, error); + } /* while */ + } else { /* No headers and footers, translate the blob */ + der = nss_ZNEW(NULL, SECItem); + rv = ATOB_ConvertAsciiToItem(der, asc); + if (rv) { + goto loser; + } + } + + nss_ZFreeIf(filedata.data); + filedata.data = 0; + filedata.len = 0; + } else { + /* Read in binary der */ + rv = FileToItem(der, inFile); + if (rv) { + PR_Close(inFile); + return -1; + } + } + PR_Close(inFile); + return count; + + loser: + if (filedata.len > 0) + nss_ZFreeIf(filedata.data); + PR_Close(inFile); + return -1; +} + +FILE *plogfile; + +void open_log() +{ +#ifdef DEBUG + plogfile = fopen("/tmp/pkcs11.log", "a"); +#endif + + return; +} + +void close_log() +{ +#ifdef DEBUG + fclose(plogfile); +#endif + return; +} + +void plog(const char *fmt, ...) +{ +#ifdef DEBUG + va_list ap; + + va_start(ap, fmt); + vfprintf(plogfile, fmt, ap); + va_end(ap); + + fflush(plogfile); +#endif +} -- cgit