]> git.saurik.com Git - apple/security.git/blobdiff - SecureTransport/appleCdsa.cpp
Security-54.1.3.tar.gz
[apple/security.git] / SecureTransport / appleCdsa.cpp
diff --git a/SecureTransport/appleCdsa.cpp b/SecureTransport/appleCdsa.cpp
new file mode 100644 (file)
index 0000000..987d1e9
--- /dev/null
@@ -0,0 +1,1350 @@
+/*
+ * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * The contents of this file constitute Original Code as defined in and are
+ * subject to the Apple Public Source License Version 1.2 (the 'License').
+ * You may not use this file except in compliance with the License. Please obtain
+ * a copy of the License at http://www.apple.com/publicsource and read it before
+ * using this file.
+ * 
+ * This Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
+ * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
+ * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
+ * specific language governing rights and limitations under the License.
+ */
+
+
+/*
+       File:           appleCdsa.cpp
+
+       Contains:       interface between SSL and CDSA
+
+       Written by:     Doug Mitchell
+
+       Copyright: (c) 1999 by Apple Computer, Inc., all rights reserved.
+
+*/
+
+#include "ssl.h"
+#include "sslContext.h"
+#include "sslMemory.h"
+#include "appleCdsa.h"
+#include "sslUtils.h"
+#include "sslDebug.h"
+#include "sslBER.h"
+#include "ModuleAttacher.h"
+
+#ifndef        _SSL_KEYCHAIN_H_
+#include "sslKeychain.h"
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <Security/cssm.h>
+#include <Security/cssmapple.h>
+#include <Security/cssmerrno.h>
+
+/* X.509 includes, from cssmapi */
+#include <Security/x509defs.h>         /* x.509 function and type defs */
+#include <Security/oidsalg.h>
+#include <Security/oidscert.h>
+
+#pragma mark *** Utilities ***
+
+/*
+ * Set up a Raw symmetric key with specified algorithm and key bits.
+ */
+OSStatus sslSetUpSymmKey(
+       CSSM_KEY_PTR    symKey,
+       CSSM_ALGORITHMS alg,
+       CSSM_KEYUSE             keyUse,                 // CSSM_KEYUSE_ENCRYPT, etc.
+       CSSM_BOOL               copyKey,                // true: copy keyData   false: set by reference
+       uint8                   *keyData,
+       uint32                  keyDataLen)             // in bytes
+{
+       OSStatus serr;
+       CSSM_KEYHEADER *hdr;
+       
+       memset(symKey, 0, sizeof(CSSM_KEY));
+       if(copyKey) {
+               serr = stSetUpCssmData(&symKey->KeyData, keyDataLen);
+               if(serr) {
+                       return serr;
+               }
+               memmove(symKey->KeyData.Data, keyData, keyDataLen);
+       }
+       else {
+               symKey->KeyData.Data = keyData;
+               symKey->KeyData.Length = keyDataLen;
+       }
+       
+       /* set up the header */
+       hdr = &symKey->KeyHeader;
+       hdr->BlobType = CSSM_KEYBLOB_RAW;
+       hdr->Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
+       hdr->AlgorithmId = alg;
+       hdr->KeyClass = CSSM_KEYCLASS_SESSION_KEY;
+       hdr->LogicalKeySizeInBits = keyDataLen * 8;
+       hdr->KeyAttr = CSSM_KEYATTR_MODIFIABLE | CSSM_KEYATTR_EXTRACTABLE;
+       hdr->KeyUsage = keyUse;
+       hdr->WrapAlgorithmId = CSSM_ALGID_NONE;
+       return noErr;
+}
+
+/*
+ * Free a CSSM_KEY - its CSP resources, KCItemRef, and the key itself.
+ */
+OSStatus sslFreeKey(
+       CSSM_CSP_HANDLE         cspHand,
+       CSSM_KEY_PTR            *key,           /* so we can null it out */
+       #if             ST_KC_KEYS_NEED_REF
+       SecKeychainRef  *kcItem)
+       #else   
+       void                    *kcItem) 
+       #endif
+{
+       assert(key != NULL);
+       
+       if(*key != NULL) {
+               if(cspHand != 0) {
+                       CSSM_FreeKey(cspHand, NULL, *key, CSSM_FALSE);
+               }
+               stAppFree(*key, NULL);          // key mallocd by CL using our callback
+               *key = NULL;
+       }
+       #if             ST_KC_KEYS_NEED_REF
+       if((kcItem != NULL) && (*kcItem != NULL)) {
+               KCReleaseItem(kcItem);          /* does this NULL the referent? */
+               *kcItem = NULL;
+       }
+       #endif
+       return noErr;
+}
+
+/*
+ * Standard app-level memory functions required by CDSA.
+ */
+void * stAppMalloc (uint32 size, void *allocRef) {
+       return( malloc(size) );
+}
+void stAppFree (void *mem_ptr, void *allocRef) {
+       free(mem_ptr);
+       return;
+}
+void * stAppRealloc (void *ptr, uint32 size, void *allocRef) {
+       return( realloc( ptr, size ) );
+}
+void * stAppCalloc (uint32 num, uint32 size, void *allocRef) {
+       return( calloc( num, size ) );
+}
+
+/*
+ * Ensure there's a connection to ctx->cspHand. If there 
+ * already is one, fine.
+ * Note that as of 12/18/00, we assume we're connected to 
+ * all modules all the time (since we do an attachToAll() in 
+ * SSLNewContext()).
+ */
+OSStatus attachToCsp(SSLContext *ctx)
+{
+       assert(ctx != NULL);
+       if(ctx->cspHand != 0) {
+               return noErr;
+       }       
+       else {
+               return errSSLModuleAttach;
+       }
+}
+
+/* 
+ * Connect to TP, CL; reusable.
+ */
+OSStatus attachToCl(SSLContext *ctx)
+{
+       assert(ctx != NULL);
+       if(ctx->clHand != 0) {
+               return noErr;
+       }
+       else {
+               return errSSLModuleAttach;
+       }
+}
+
+OSStatus attachToTp(SSLContext *ctx)
+{
+       assert(ctx != NULL);
+       if(ctx->tpHand != 0) {
+               return noErr;
+       }
+       else {
+               return errSSLModuleAttach;
+       }
+}
+
+/*
+ * Convenience function - attach to CSP, CL, TP. Reusable. 
+ */
+OSStatus attachToAll(SSLContext *ctx)
+{
+       CSSM_RETURN crtn;
+       
+       assert(ctx != NULL);
+       crtn = attachToModules(&ctx->cspHand, &ctx->clHand, &ctx->tpHand);
+       if(crtn) {
+          return errSSLModuleAttach;
+       }
+       else {
+               return noErr;
+       }
+}
+
+OSStatus detachFromAll(SSLContext *ctx)
+{
+       #if     0
+       /* No more, attachments are kept on a global basis */
+       assert(ctx != NULL);
+       if(ctx->cspHand != 0) {
+               CSSM_ModuleDetach(ctx->cspHand);
+               ctx->cspHand = 0;
+       }
+       if(ctx->tpHand != 0) {
+               CSSM_ModuleDetach(ctx->tpHand);
+               ctx->tpHand = 0;
+       }
+       if(ctx->clHand != 0) {
+               CSSM_ModuleDetach(ctx->clHand);
+               ctx->clHand = 0;
+       }
+       #endif  /* 0 */
+       return noErr;
+}
+
+#pragma mark -
+#pragma mark *** CSSM_DATA routines ***
+
+CSSM_DATA_PTR stMallocCssmData(
+       uint32 size)
+{
+       CSSM_DATA_PTR rtn = (CSSM_DATA_PTR)stAppMalloc(sizeof(CSSM_DATA), NULL);
+
+       if(rtn == NULL) {
+               return NULL;
+       }
+       rtn->Length = size;
+       if(size == 0) {
+               rtn->Data = NULL;
+       }
+       else {
+               rtn->Data = (uint8 *)stAppMalloc(size, NULL);
+       }
+       return rtn;
+}
+
+void stFreeCssmData(
+       CSSM_DATA_PTR data,
+       CSSM_BOOL freeStruct)
+{
+       if(data == NULL) {
+               return;
+       }
+       if(data->Data != NULL) {
+               stAppFree(data->Data, NULL);
+               data->Data   = NULL;
+       }
+       data->Length = 0;
+       if(freeStruct) {
+               stAppFree(data, NULL);
+       }
+}
+
+/*
+ * Ensure that indicated CSSM_DATA_PTR can handle 'length' bytes of data.
+ * Malloc the Data ptr if necessary.
+ */
+OSStatus stSetUpCssmData(
+       CSSM_DATA_PTR   data,
+       uint32                  length)
+{
+       assert(data != NULL);
+       if(data->Length == 0) {
+               data->Data = (uint8 *)stAppMalloc(length, NULL);
+               if(data->Data == NULL) {
+                       return memFullErr;
+               }
+       }
+       else if(data->Length < length) {
+               sslErrorLog("stSetUpCssmData: length too small\n");
+               return memFullErr;
+       }
+       data->Length = length;
+       return noErr;
+}
+
+#pragma mark -
+#pragma mark *** Public CSP Functions ***
+
+/*
+ * Raw RSA sign/verify.
+ *
+ * Initial X port: CSP doesns't support this, so we'll do sign/verify via
+ * raw RSA encrypt/decrypt here. 
+ */
+#define SIGN_VFY_VIA_ENCR_DECR 0
+
+#if            SIGN_VFY_VIA_ENCR_DECR
+
+OSStatus sslRsaRawSign(
+       SSLContext                      *ctx,
+       const CSSM_KEY          *privKey,
+       CSSM_CSP_HANDLE         cspHand,
+       const UInt8                     *plainText,
+       UInt32                          plainTextLen,
+       UInt8                           *sig,                   // mallocd by caller; RETURNED
+       UInt32                          sigLen,                 // available
+       UInt32                          *actualBytes)   // RETURNED
+{
+       /* Raw RSA sign with no digest is the same as raw RSA encrypt. */
+       /* Force CSSM_KEYUSE_ANY in case CL provided keyuse bits more specific 
+        * than we really want */
+       OSStatus serr;
+       CSSM_KEYUSE savedKeyUse = privKey->KeyHeader.KeyUsage;
+       privKey->KeyHeader.KeyUsage = CSSM_KEYUSE_ANY;
+       serr = sslRsaEncrypt(ctx,
+               privKey,
+               cspHand,
+               plainText,
+               plainTextLen,
+               sig,    
+               sigLen,
+               actualBytes);
+       privKey->KeyHeader.KeyUsage = savedKeyUse;
+       return serr;
+}
+
+OSStatus sslRsaRawVerify(
+       SSLContext                      *ctx,
+       const CSSM_KEY          *pubKey,
+       CSSM_CSP_HANDLE         cspHand,
+       const UInt8                     *plainText,
+       UInt32                          plainTextLen,
+       const UInt8                     *sig,
+       UInt32                          sigLen) 
+{      
+       /* 
+        * Raw RSA verify with no digest is just a comparison of the incoming
+        * plaintext with (signature, decrypted via raw RSA decrypt). 
+        */
+        
+       UInt32 actualBytes;
+       OSStatus serr;
+       UInt8  *digest;
+       
+       /* Force CSSM_KEYUSE_ANY in case CL provided keyuse bits more specific 
+        * than we really want */
+       CSSM_KEYUSE savedKeyUse = pubKey->KeyHeader.KeyUsage;
+       pubKey->KeyHeader.KeyUsage = CSSM_KEYUSE_ANY;
+       
+       /* malloc space for decrypting the signature */
+       digest = sslMalloc(plainTextLen);
+       if(digest == NULL) {
+               return memFullErr;
+       }
+       
+       /* decrypt signature */
+       serr = sslRsaDecrypt(ctx,
+               pubKey,
+               cspHand,
+               sig,
+               sigLen,         
+               digest,                 
+               plainTextLen,   
+               &actualBytes);
+       pubKey->KeyHeader.KeyUsage = savedKeyUse;
+       if(serr) {
+               goto errOut;
+       }
+       if((actualBytes != plainTextLen) ||
+          (memcmp(plainText, digest, plainTextLen))) {
+               sslErrorLog("sslRsaRawVerify: sig miscompare\n");
+               serr = errSSLCrypto;
+       }
+       else {
+               serr = noErr;
+       }
+errOut:
+       sslFree(digest);
+       return serr;
+}
+
+#else  /* OS9 and future post-cheetah version */
+
+OSStatus sslRsaRawSign(
+       SSLContext                      *ctx,
+       const CSSM_KEY          *privKey,
+       CSSM_CSP_HANDLE         cspHand,
+       const UInt8                     *plainText,
+       UInt32                          plainTextLen,
+       UInt8                           *sig,                   // mallocd by caller; RETURNED
+       UInt32                          sigLen,                 // available
+       UInt32                          *actualBytes)   // RETURNED
+{
+       CSSM_CC_HANDLE                  sigHand = 0;
+       CSSM_RETURN                             crtn;
+       OSStatus                                        serr;
+       CSSM_DATA                               sigData;
+       CSSM_DATA                               ptextData;
+       
+       assert(ctx != NULL);
+       if((privKey == NULL)    || 
+          (cspHand == 0)               || 
+          (plainText == NULL)  || 
+          (sig == NULL)                ||
+          (actualBytes == NULL)) {
+               sslErrorLog("sslRsaRawSign: bad arguments\n");
+               return errSSLInternal;
+       }
+       *actualBytes = 0;
+       
+       crtn = CSSM_CSP_CreateSignatureContext(cspHand,
+               CSSM_ALGID_RSA,
+               NULL,                           // passPhrase
+               privKey,
+               &sigHand);
+       if(crtn) {
+               stPrintCdsaError("CSSM_CSP_CreateSignatureContext (1)", crtn);
+               return errSSLCrypto;
+       }
+       
+       ptextData.Data = (uint8 *)plainText;
+       ptextData.Length = plainTextLen;
+       
+       /* caller better get this right, or the SignData will fail */
+       sigData.Data = sig;
+       sigData.Length = sigLen;
+       
+       crtn = CSSM_SignData(sigHand,
+               &ptextData,
+               1,
+               CSSM_ALGID_NONE,        // digestAlg 
+               &sigData);
+       if(crtn) {
+               stPrintCdsaError("CSSM_SignData", crtn);
+               serr = errSSLCrypto;
+       }
+       else {
+               *actualBytes = sigData.Length;
+               serr = noErr;
+       }
+       if(sigHand != 0) {
+               CSSM_DeleteContext(sigHand);
+       }
+       return serr;
+}
+
+OSStatus sslRsaRawVerify(
+       SSLContext                      *ctx,
+       const CSSM_KEY          *pubKey,
+       CSSM_CSP_HANDLE         cspHand,
+       const UInt8                     *plainText,
+       UInt32                          plainTextLen,
+       const UInt8                     *sig,
+       UInt32                          sigLen) 
+{
+       CSSM_CC_HANDLE                  sigHand = 0;
+       CSSM_RETURN                             crtn;
+       OSStatus                                        serr;
+       CSSM_DATA                               sigData;
+       CSSM_DATA                               ptextData;
+       
+       assert(ctx != NULL);
+       if((pubKey == NULL)     || 
+          (cspHand == 0)               || 
+          (plainText == NULL)  || 
+          (sig == NULL)) {
+               sslErrorLog("sslRsaRawVerify: bad arguments\n");
+               return errSSLInternal;
+       }
+       
+       crtn = CSSM_CSP_CreateSignatureContext(cspHand,
+               CSSM_ALGID_RSA,
+               NULL,                           // passPhrase
+               pubKey,
+               &sigHand);
+       if(sigHand == 0) {
+               stPrintCdsaError("CSSM_CSP_CreateSignatureContext (2)", crtn);
+               return errSSLCrypto;
+       }
+       
+       ptextData.Data = (uint8 *)plainText;
+       ptextData.Length = plainTextLen;
+       sigData.Data = (uint8 *)sig;
+       sigData.Length = sigLen;
+       
+       crtn = CSSM_VerifyData(sigHand,
+               &ptextData,
+               1,
+               CSSM_ALGID_NONE,                // digestAlg
+               &sigData);
+       if(crtn) {
+               stPrintCdsaError("CSSM_VerifyData", crtn);
+               serr = errSSLCrypto;
+       }
+       else {
+               serr = noErr;
+       }
+       if(sigHand != 0) {
+               CSSM_DeleteContext(sigHand);
+       }
+       return serr;
+}
+#endif /* SIGN_VFY_VIA_ENCR_DECR */
+
+/*
+ * Encrypt/Decrypt
+ */
+OSStatus sslRsaEncrypt(
+       SSLContext                      *ctx,
+       const CSSM_KEY          *pubKey,
+       CSSM_CSP_HANDLE         cspHand,
+       const UInt8                     *plainText,
+       UInt32                          plainTextLen,
+       UInt8                           *cipherText,            // mallocd by caller; RETURNED 
+       UInt32                          cipherTextLen,          // available
+       UInt32                          *actualBytes)           // RETURNED
+{
+       CSSM_DATA               ctextData = {0, NULL};
+       CSSM_DATA               ptextData;
+       CSSM_DATA               remData = {0, NULL};
+       CSSM_CC_HANDLE  cryptHand = 0;
+       OSStatus                        serr = errSSLInternal;
+       CSSM_RETURN             crtn;
+       uint32                  bytesMoved = 0;
+       CSSM_ACCESS_CREDENTIALS creds;
+       
+       assert(ctx != NULL);
+       assert(actualBytes != NULL);
+       *actualBytes = 0;
+       
+       if((pubKey == NULL) || (cspHand == 0)) {
+               sslErrorLog("sslRsaEncrypt: bad pubKey/cspHand\n");
+               return errSSLInternal;
+       }
+       
+       #if             RSA_PUB_KEY_USAGE_HACK
+       ((CSSM_KEY_PTR)pubKey)->KeyHeader.KeyUsage |= CSSM_KEYUSE_ENCRYPT;
+       #endif
+       memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
+       
+       crtn = CSSM_CSP_CreateAsymmetricContext(cspHand,
+               CSSM_ALGID_RSA,
+               &creds,
+               pubKey,
+               CSSM_PADDING_PKCS1,
+               &cryptHand);
+       if(crtn) {
+               stPrintCdsaError("CSSM_CSP_CreateAsymmetricContext", crtn);
+               return errSSLCrypto;
+       }
+       ptextData.Data = (uint8 *)plainText;
+       ptextData.Length = plainTextLen;
+       
+       if(pubKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY) {
+               /* 
+                * Special case, encrypting with private key (i.e., raw sign). Add
+                * the required context attr.
+                */
+               CSSM_CONTEXT_ATTRIBUTE  modeAttr;
+               
+               modeAttr.AttributeType     = CSSM_ATTRIBUTE_MODE;
+               modeAttr.AttributeLength   = sizeof(uint32);
+               modeAttr.Attribute.Uint32  = CSSM_ALGMODE_PRIVATE_KEY;
+               crtn = CSSM_UpdateContextAttributes(cryptHand, 1, &modeAttr);
+               if(crtn) {
+                       stPrintCdsaError("CSSM_UpdateContextAttributes", crtn);
+                       CSSM_DeleteContext(cryptHand);
+                       return errSSLCrypto;
+               }
+       }
+       
+       /* 
+        * Have CSP malloc ciphertext 
+        */
+       crtn = CSSM_EncryptData(cryptHand,
+               &ptextData,
+               1,
+               &ctextData,
+               1,
+               &bytesMoved,
+               &remData);
+       if(crtn == CSSM_OK) {
+               /* 
+                * ciphertext in both ctextData and remData; ensure it'll fit
+                * in caller's buf & copy 
+                */
+               if(bytesMoved > cipherTextLen) {
+                       sslErrorLog("sslRsaEncrypt overflow; cipherTextLen %ld bytesMoved %ld\n",
+                               cipherTextLen, bytesMoved);
+                       serr = errSSLCrypto;
+               }
+               else {
+                       UInt32 toMoveCtext;
+                       UInt32 toMoveRem;
+                       
+                       *actualBytes = bytesMoved;
+                       /* 
+                        * Snag valid data from ctextData - its length or bytesMoved, 
+                        * whichever is less
+                        */
+                       if(ctextData.Length > bytesMoved) {
+                               /* everything's in ctext */
+                               toMoveCtext = bytesMoved;
+                               toMoveRem = 0;
+                       }
+                       else {
+                               /* must be some in remData too */
+                               toMoveCtext = ctextData.Length;
+                               toMoveRem = bytesMoved - toMoveCtext;           // remainder 
+                       }
+                       if(toMoveCtext) {
+                               memmove(cipherText, ctextData.Data, toMoveCtext);
+                       }
+                       if(toMoveRem) {
+                               memmove(cipherText + toMoveCtext, remData.Data,
+                                       toMoveRem);
+                       }
+                       serr = noErr;
+               }
+       }
+       else {
+               stPrintCdsaError("CSSM_EncryptData", crtn);
+               serr = errSSLCrypto;
+       }
+       if(cryptHand != 0) {
+               CSSM_DeleteContext(cryptHand);
+       }
+
+       /* free data mallocd by CSP */
+       stFreeCssmData(&ctextData, CSSM_FALSE);
+       stFreeCssmData(&remData, CSSM_FALSE);
+       return serr;
+}
+
+OSStatus sslRsaDecrypt(
+       SSLContext                      *ctx,
+       const CSSM_KEY          *privKey,
+       CSSM_CSP_HANDLE         cspHand,
+       const UInt8                     *cipherText,
+       UInt32                          cipherTextLen,          
+       UInt8                           *plainText,                     // mallocd by caller; RETURNED
+       UInt32                          plainTextLen,           // available
+       UInt32                          *actualBytes)           // RETURNED
+{
+       CSSM_DATA               ptextData = {0, NULL};
+       CSSM_DATA               ctextData;
+       CSSM_DATA               remData = {0, NULL};
+       CSSM_CC_HANDLE  cryptHand = 0;
+       OSStatus                        serr = errSSLInternal;
+       CSSM_RETURN             crtn;
+       uint32                  bytesMoved = 0;
+       CSSM_ACCESS_CREDENTIALS creds;
+               
+       assert(ctx != NULL);
+       assert(actualBytes != NULL);
+       *actualBytes = 0;
+       
+       if((privKey == NULL) || (cspHand == 0)) {
+               sslErrorLog("sslRsaDecrypt: bad privKey/cspHand\n");
+               return errSSLInternal;
+       }
+       memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
+       crtn = CSSM_CSP_CreateAsymmetricContext(cspHand,
+               CSSM_ALGID_RSA,
+               &creds,
+               privKey,
+               CSSM_PADDING_PKCS1,
+               &cryptHand);
+       if(crtn) {
+               stPrintCdsaError("CSSM_CSP_CreateAsymmetricContext", crtn);
+               return errSSLCrypto;
+       }
+       ctextData.Data = (uint8 *)cipherText;
+       ctextData.Length = cipherTextLen;
+       
+       if(privKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PUBLIC_KEY) {
+               /* 
+                * Special case, decrypting with public key (i.e., raw verify). Add
+                * the required context attr.
+                */
+               CSSM_CONTEXT_ATTRIBUTE  modeAttr;
+               
+               modeAttr.AttributeType     = CSSM_ATTRIBUTE_MODE;
+               modeAttr.AttributeLength   = sizeof(uint32);
+               modeAttr.Attribute.Uint32  = CSSM_ALGMODE_PUBLIC_KEY;
+               crtn = CSSM_UpdateContextAttributes(cryptHand, 1, &modeAttr);
+               if(crtn) {
+                       stPrintCdsaError("CSSM_UpdateContextAttributes", crtn);
+                       CSSM_DeleteContext(cryptHand);
+                       return errSSLCrypto;
+               }
+       }
+
+       /* 
+        * Have CSP malloc plaintext 
+        */
+       crtn = CSSM_DecryptData(cryptHand,
+               &ctextData,
+               1,
+               &ptextData,
+               1,
+               &bytesMoved,
+               &remData);
+       if(crtn == CSSM_OK) {
+               /* 
+                * plaintext in both ptextData and remData; ensure it'll fit
+                * in caller's buf & copy 
+                */
+               if(bytesMoved > plainTextLen) {
+                       sslErrorLog("sslRsaDecrypt overflow; plainTextLen %ld bytesMoved %ld\n",
+                               plainTextLen, bytesMoved);
+                       serr = errSSLCrypto;
+               }
+               else {
+                       UInt32 toMovePtext;
+                       UInt32 toMoveRem;
+                       
+                       *actualBytes = bytesMoved;
+                       /* 
+                        * Snag valid data from ptextData - its length or bytesMoved, 
+                        * whichever is less
+                        */
+                       if(ptextData.Length > bytesMoved) {
+                               /* everything's in ptext */
+                               toMovePtext = bytesMoved;
+                               toMoveRem = 0;
+                       }
+                       else {
+                               /* must be some in remData too */
+                               toMovePtext = ptextData.Length;
+                               toMoveRem = bytesMoved - toMovePtext;           // remainder 
+                       }
+                       if(toMovePtext) {
+                               memmove(plainText, ptextData.Data, toMovePtext);
+                       }
+                       if(toMoveRem) {
+                               memmove(plainText + toMovePtext, remData.Data,
+                                       toMoveRem);
+                       }
+                       serr = noErr;
+               }
+       }
+       else {
+               stPrintCdsaError("CSSM_DecryptData", crtn);
+               serr = errSSLCrypto;
+       }
+       if(cryptHand != 0) {
+               CSSM_DeleteContext(cryptHand);
+       }
+       
+       /* free data mallocd by CSP */
+       stFreeCssmData(&ptextData, CSSM_FALSE);
+       stFreeCssmData(&remData, CSSM_FALSE);
+       return serr;
+}
+
+/*
+ * Obtain size of key in bytes.
+ */
+UInt32 sslKeyLengthInBytes(const CSSM_KEY *key)
+{
+       assert(key != NULL);
+       return (((key->KeyHeader.LogicalKeySizeInBits) + 7) / 8);
+}
+
+/*
+ * Get raw key bits from an RSA public key.
+ */
+OSStatus sslGetPubKeyBits(
+       SSLContext                      *ctx,
+       const CSSM_KEY          *pubKey,
+       CSSM_CSP_HANDLE         cspHand,
+       SSLBuffer                       *modulus,               // data mallocd and RETURNED
+       SSLBuffer                       *exponent)              // data mallocd and RETURNED
+{
+       CSSM_KEY                        wrappedKey;
+       CSSM_BOOL                       didWrap = CSSM_FALSE;
+       const CSSM_KEYHEADER *hdr;
+       CSSM_CC_HANDLE          ccHand;
+       CSSM_RETURN                     crtn;
+       SSLBuffer                       pubKeyBlob;
+       OSStatus                                srtn;
+       CSSM_ACCESS_CREDENTIALS creds;
+       
+       assert(ctx != NULL);
+       assert(modulus != NULL);
+       assert(exponent != NULL);
+       assert(pubKey != NULL);
+       
+       hdr = &pubKey->KeyHeader;
+       if(hdr->KeyClass != CSSM_KEYCLASS_PUBLIC_KEY) {
+               sslErrorLog("sslGetPubKeyBits: bad keyClass (%ld)\n", hdr->KeyClass);
+               return errSSLInternal;
+       }
+       if(hdr->AlgorithmId != CSSM_ALGID_RSA) {
+               sslErrorLog("sslGetPubKeyBits: bad AlgorithmId (%ld)\n", hdr->AlgorithmId);
+               return errSSLInternal;
+       }
+       
+       /* 
+        * Handle possible reference format - I think it should be in
+        * blob form since it came from the DL, but conversion is 
+        * simple.
+        */
+       switch(hdr->BlobType) {
+               case CSSM_KEYBLOB_RAW:
+                       /* easy case */
+                       CSSM_TO_SSLBUF(&pubKey->KeyData, &pubKeyBlob);
+                       break;
+
+               case CSSM_KEYBLOB_REFERENCE:
+                       /* 
+                        * Convert to a blob via "NULL wrap"; no wrapping key, 
+                        * ALGID_NONE 
+                        */ 
+                       srtn = attachToCsp(ctx);
+                       if(srtn) {
+                               return srtn;
+                       }
+                       memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
+                       crtn = CSSM_CSP_CreateSymmetricContext(ctx->cspHand,
+                                       CSSM_ALGID_NONE,
+                                       CSSM_ALGMODE_NONE,
+                                       &creds,                 // creds
+                                       pubKey,
+                                       NULL,                   // InitVector
+                                       CSSM_PADDING_NONE,
+                                       0,                      // reserved
+                                       &ccHand);
+                       if(crtn) {
+                               stPrintCdsaError("sslGetPubKeyBits: CreateSymmetricContext failure", crtn); 
+                               return errSSLCrypto;
+                       }
+                       memset(&wrappedKey, 0, sizeof(CSSM_KEY));
+                       crtn = CSSM_WrapKey(ccHand,
+                               &creds,
+                               pubKey,
+                               NULL,                   // descriptiveData
+                               &wrappedKey);
+                       CSSM_DeleteContext(ccHand);
+                       if(crtn) {
+                               stPrintCdsaError("CSSM_WrapKey", crtn);
+                               return errSSLCrypto;
+                       }
+                       hdr = &wrappedKey.KeyHeader;
+                       if(hdr->BlobType != CSSM_KEYBLOB_RAW) {
+                               sslErrorLog("sslGetPubKeyBits: bad BlobType (%ld) after WrapKey\n", 
+                                       hdr->BlobType);
+                               return errSSLCrypto;
+                       }
+                       didWrap = CSSM_TRUE;
+                       CSSM_TO_SSLBUF(&wrappedKey.KeyData, &pubKeyBlob);
+                       break;
+
+               default:
+                       sslErrorLog("sslGetPubKeyBits: bad BlobType (%ld)\n", 
+                               hdr->BlobType);
+                       return errSSLInternal;
+       
+       }       /* switch BlobType */
+
+       assert(hdr->BlobType == CSSM_KEYBLOB_RAW); 
+       srtn = sslDecodeRsaBlob(&pubKeyBlob, modulus, exponent);
+       if(didWrap) {
+               CSSM_FreeKey(ctx->cspHand, NULL, &wrappedKey, CSSM_FALSE);
+       }
+       return srtn;
+}
+
+/*
+ * Given raw RSA key bits, cook up a CSSM_KEY_PTR. Used in 
+ * Server-initiated key exchange. 
+ */
+OSStatus sslGetPubKeyFromBits(
+       SSLContext                      *ctx,
+       const SSLBuffer         *modulus,       
+       const SSLBuffer         *exponent,      
+       CSSM_KEY_PTR            *pubKey,                // mallocd and RETURNED
+       CSSM_CSP_HANDLE         *cspHand)               // RETURNED
+{
+       CSSM_KEY_PTR            key = NULL;
+       OSStatus                                serr;
+       SSLBuffer                       blob;
+       CSSM_KEYHEADER_PTR      hdr;
+       CSSM_KEY_SIZE           keySize;
+       CSSM_RETURN                     crtn;
+       
+       assert((ctx != NULL) && (modulus != NULL) && (exponent != NULL));
+       assert((pubKey != NULL) && (cspHand != NULL));
+       
+       *pubKey = NULL;
+       *cspHand = 0;
+       
+       serr = attachToCsp(ctx);
+       if(serr) {
+               return serr;
+       }
+       serr = sslEncodeRsaBlob(modulus, exponent, &blob);
+       if(serr) {
+               return serr;
+       }
+       
+       /* the rest is boilerplate, cook up a good-looking public key */
+       key = (CSSM_KEY_PTR)sslMalloc(sizeof(CSSM_KEY));
+       if(key == NULL) {
+               return memFullErr;
+       }
+       memset(key, 0, sizeof(CSSM_KEY));
+       hdr = &key->KeyHeader;
+       
+    hdr->HeaderVersion = CSSM_KEYHEADER_VERSION;
+    /* key_ptr->KeyHeader.CspId is unknown (remains 0) */
+    hdr->BlobType = CSSM_KEYBLOB_RAW;
+    hdr->AlgorithmId = CSSM_ALGID_RSA;
+    hdr->Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1;
+    hdr->KeyClass = CSSM_KEYCLASS_PUBLIC_KEY;
+    /* comply with ASA requirements */
+    hdr->KeyUsage = CSSM_KEYUSE_VERIFY;
+    hdr->KeyAttr = CSSM_KEYATTR_EXTRACTABLE;
+    /* key_ptr->KeyHeader.StartDate is unknown  (remains 0) */
+    /* key_ptr->KeyHeader.EndDate is unknown  (remains 0) */
+    hdr->WrapAlgorithmId = CSSM_ALGID_NONE;
+    hdr->WrapMode = CSSM_ALGMODE_NONE;
+
+       /* blob->data was mallocd by sslEncodeRsaBlob, pass it over to 
+        * actual key */
+       SSLBUF_TO_CSSM(&blob, &key->KeyData);
+       
+       /*
+        * Get keySizeInBits. This also serves to validate the key blob
+        * we just cooked up.
+        */
+    crtn = CSSM_QueryKeySizeInBits(ctx->cspHand, CSSM_INVALID_HANDLE, key, &keySize);
+       if(crtn) {      
+       stPrintCdsaError("sslGetPubKeyFromBits: QueryKeySizeInBits\n", crtn);
+               serr = errSSLCrypto;
+       goto abort;
+       }
+       
+       /* success */
+    hdr->LogicalKeySizeInBits = keySize.EffectiveKeySizeInBits;
+    *pubKey = key;
+    *cspHand = ctx->cspHand;
+       return noErr;
+       
+abort:
+       /* note this frees the blob */
+       sslFreeKey(ctx->cspHand, &key, NULL);
+       return serr;
+}
+
+#pragma mark -
+#pragma mark *** Public Certificate Functions ***
+
+/*
+ * Given a DER-encoded cert, obtain its public key as a CSSM_KEY_PTR.
+ * Caller must CSSM_FreeKey and free the CSSM_KEY_PTR itself. 
+ *
+ * For now, the returned cspHand is a copy of ctx->cspHand, so it
+ * doesn't have to be detached later - this may change.
+ *
+ * Update: since CSSM_CL_CertGetKeyInfo() doesn't provide a means for
+ * us to tell the CL what CSP to use, we really have no way of knowing 
+ * what is going on here...we return the process-wide (bare) cspHand,
+ * which is currently always able to deal with this raw public key. 
+ */
+OSStatus sslPubKeyFromCert(
+       SSLContext                      *ctx,
+       const SSLBuffer         &derCert,
+       CSSM_KEY_PTR            *pubKey,                // RETURNED
+       CSSM_CSP_HANDLE         *cspHand)               // RETURNED
+{
+       OSStatus                        serr;
+       CSSM_DATA               certData;
+       CSSM_RETURN             crtn;
+       
+       assert(ctx != NULL);
+       assert(pubKey != NULL);
+       assert(cspHand != NULL);
+       
+       *pubKey = NULL;
+       *cspHand = 0;
+       
+       serr = attachToCl(ctx);
+       if(serr) {
+               return serr;
+       }
+       serr = attachToCsp(ctx);
+       if(serr) {
+               return serr;
+       }
+       SSLBUF_TO_CSSM(&derCert, &certData);
+       crtn = CSSM_CL_CertGetKeyInfo(ctx->clHand, &certData, pubKey);
+       if(crtn) {
+               return errSSLBadCert;
+       }
+       else {
+               *cspHand = ctx->cspHand; 
+               return noErr;
+       }
+}
+
+#if            ST_MANAGES_TRUSTED_ROOTS
+
+/*
+ * Given a CSSM_CERTGROUP which fails due to CSSM_TP_INVALID_ANCHOR
+ * (chain verifies to an unknown root):
+ *
+ * -- find the root cert
+ * -- add it to newRootCertKc if present (else error)
+ * -- add it to trustedCerts
+ * -- re-verify certgroup, demand full success
+ */
+static OSStatus sslHandleNewRoot(
+       SSLContext                              *ctx,
+       CSSM_CERTGROUP_PTR              certGroup)
+{
+       int                     i;
+       CSSM_DATA_PTR   rootCert;
+       CSSM_BOOL               expired;
+       OSStatus                        serr;
+       CSSM_BOOL               brtn;
+       
+       assert(ctx != NULL);
+       assert(certGroup != NULL);
+       
+       if(ctx->newRootCertKc == NULL) {
+               /* no place to add this; done */
+               return errSSLUnknownRootCert;
+       }
+       
+       /*
+        * The root cert "should" be at the end of the chain, but 
+        * let's not assume that. (We are assuming that there is 
+        * only one root in the cert group...)
+        */
+       for(i=0; i<certGroup->NumCerts; i++) {
+               rootCert = &certGroup->CertList[i];
+               if(sslVerifyCert(ctx, rootCert, rootCert, ctx->cspHand, &expired)) {
+                       break;
+               }
+       }
+       if(i == certGroup->NumCerts) {
+               /* Huh! no root cert!? We should not have been called! */
+               sslErrorLog("sslHandleNewRoot: no root cert!\n");
+               return errSSLInternal;
+       }
+       
+       /*
+        * Add to newRootCertKc. This may well fail due to user interaction.    
+        */
+       serr = sslAddNewRoot(ctx, rootCert);
+       if(serr) {
+               return serr;
+       }
+       
+       /*
+        * Just to be sure...reverify the whole cert chain. 
+        */
+       brtn = CSSM_TP_CertGroupVerify(
+               ctx->tpHand,
+               ctx->clHand,
+               ctx->cspHand,
+               NULL,                                   // DBList
+               NULL,                                   // PolicyIdentifiers
+               0,                                              // NumberofPolicyIdentifiers
+               CSSM_TP_STOP_ON_POLICY, 
+               certGroup,
+               ctx->trustedCerts,              // AnchorCerts
+               ctx->numTrustedCerts, 
+               NULL,                                   // VerifyScope
+               0,                                              // ScopeSize
+               0,                                              // Action
+               0,                                              // Data
+               NULL,                                   // evidence
+               NULL);                                  // evidenceSize
+       if(brtn == CSSM_FALSE) {
+               sslErrorLog("sslHandleNewRoot: adding new root did not help!\n");
+               return errSSLUnknownRootCert;
+       }
+       return noErr;
+}
+
+#endif /* ST_MANAGES_TRUSTED_ROOTS */
+
+/*
+ * Verify a chain of DER-encoded certs.
+ * First cert in a chain is root; this must also be present
+ * in ctx->trustedCerts. 
+ */
+OSStatus sslVerifyCertChain(
+       SSLContext                              *ctx,
+       const SSLCertificate    &certChain,
+       bool                                    verifyHostName /* = true */) 
+{
+       UInt32                                          numCerts;
+       CSSM_CERTGROUP                          certGroup;
+       int                                             i;
+       OSStatus                                                serr;
+       SSLCertificate                          *c = (SSLCertificate *)&certChain;
+       CSSM_RETURN                                     crtn;
+       CSSM_TP_VERIFY_CONTEXT          vfyCtx;
+       CSSM_TP_CALLERAUTH_CONTEXT      authCtx;
+       CSSM_FIELD                                      policyId;
+       CSSM_DL_DB_LIST                         dbList;
+       CSSM_APPLE_TP_SSL_OPTIONS       sslOpts;
+       CSSM_APPLE_TP_ACTION_DATA       actionData;
+       
+       if(!ctx->enableCertVerify) {
+               /* trivial case, this is caller's responsibility */
+               return noErr;
+       }
+       numCerts = SSLGetCertificateChainLength(&certChain);
+       if(numCerts == 0) {
+               /* nope */
+               return errSSLBadCert;
+       }
+       #if 0
+       serr = attachToAll(ctx);
+       if(serr) {
+               return serr;
+       }
+       #endif
+       
+       /* 
+        * SSLCertificate chain --> CSSM TP cert group.
+        * TP Cert group has root at the end, opposite of 
+        * SSLCertificate chain. 
+        */
+       certGroup.GroupList.CertList = 
+               (CSSM_DATA_PTR)sslMalloc(numCerts * sizeof(CSSM_DATA));
+       if(certGroup.GroupList.CertList == NULL) {
+               return memFullErr;
+       }
+       certGroup.CertGroupType = CSSM_CERTGROUP_DATA;
+       certGroup.CertType = CSSM_CERT_X_509v3;
+       certGroup.CertEncoding = CSSM_CERT_ENCODING_DER; 
+       certGroup.NumCerts = numCerts;
+       
+       memset(certGroup.GroupList.CertList, 0, numCerts * sizeof(CSSM_DATA));
+       
+       for(i=numCerts-1; i>=0; i--) {
+               SSLBUF_TO_CSSM(&c->derCert, &certGroup.GroupList.CertList[i]);
+               c = c->next;
+       }
+       
+       memset(&vfyCtx, 0, sizeof(CSSM_TP_VERIFY_CONTEXT));
+       vfyCtx.Action = CSSM_TP_ACTION_DEFAULT;
+       vfyCtx.Cred = &authCtx;
+       
+       /* CSSM_TP_CALLERAUTH_CONTEXT components */
+       /* 
+               typedef struct cssm_tp_callerauth_context {
+                       CSSM_TP_POLICYINFO Policy;
+                       CSSM_TIMESTRING VerifyTime;
+                       CSSM_TP_STOP_ON VerificationAbortOn;
+                       CSSM_TP_VERIFICATION_RESULTS_CALLBACK CallbackWithVerifiedCert;
+                       uint32 NumberOfAnchorCerts;
+                       CSSM_DATA_PTR AnchorCerts;
+                       CSSM_DL_DB_LIST_PTR DBList;
+                       CSSM_ACCESS_CREDENTIALS_PTR CallerCredentials;
+               } CSSM_TP_CALLERAUTH_CONTEXT, *CSSM_TP_CALLERAUTH_CONTEXT_PTR;
+       */
+       
+       /* SSL-specific FieldValue */
+       sslOpts.Version = CSSM_APPLE_TP_SSL_OPTS_VERSION;
+       if(verifyHostName) {
+               sslOpts.ServerNameLen = ctx->peerDomainNameLen;
+               sslOpts.ServerName = ctx->peerDomainName;
+       }
+       else {
+               sslOpts.ServerNameLen = 0;
+               sslOpts.ServerName = NULL;
+       }
+       
+       /* TP-wide ActionData */
+       actionData.Version = CSSM_APPLE_TP_ACTION_VERSION;
+       if(ctx->numTrustedCerts != 0) {
+               /* use our anchors */
+               actionData.ActionFlags = 0;
+       }
+       else {
+               /* secret root-cert-enable */
+               actionData.ActionFlags = 0x80000000;
+       }
+       if(ctx->allowExpiredCerts) {
+               actionData.ActionFlags |= CSSM_TP_ACTION_ALLOW_EXPIRED;
+       }
+       if(ctx->allowExpiredRoots) {
+               actionData.ActionFlags |= CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT;
+       }
+       vfyCtx.ActionData.Data = (uint8 *)&actionData;
+       vfyCtx.ActionData.Length = sizeof(actionData);
+       
+       /* zero or one policy here */
+       policyId.FieldOid = CSSMOID_APPLE_TP_SSL;
+       policyId.FieldValue.Data = (uint8 *)&sslOpts;
+       policyId.FieldValue.Length = sizeof(sslOpts);
+       authCtx.Policy.NumberOfPolicyIds = 1;
+       authCtx.Policy.PolicyIds = &policyId;
+       
+       authCtx.VerifyTime = NULL;
+       authCtx.VerificationAbortOn = CSSM_TP_STOP_ON_POLICY;
+       authCtx.CallbackWithVerifiedCert = NULL;
+       authCtx.NumberOfAnchorCerts = ctx->numTrustedCerts;
+       authCtx.AnchorCerts = ctx->trustedCerts;        
+       memset(&dbList, 0, sizeof(CSSM_DL_DB_LIST));
+       authCtx.DBList = &dbList;
+       authCtx.CallerCredentials = NULL;
+
+       /*
+        * Here we go; hand it over to TP. Note trustedCerts are our
+        * known good Anchor certs; they're already formatted properly. 
+        * Unlike most other Apple code, we demand full success here, 
+        * implying that the last cert in the chain is indeed an Anchor
+        * cert. We already know that all of our anchor certs are
+        * roots, so on successful return, we'll know the incoming 
+        * chain has a root, it verifies to that root, and that that
+        * root is in trustedCerts.  
+        */
+       crtn = CSSM_TP_CertGroupVerify(ctx->tpHand,
+               ctx->clHand,
+               ctx->cspHand,
+               &certGroup,
+               &vfyCtx,
+               NULL);                  // no evidence needed
+
+       serr = noErr;
+       if(crtn) {      
+               /* get some detailed error info */
+               switch(crtn) {
+                       case CSSMERR_TP_INVALID_ANCHOR_CERT: 
+                               /* root found but we don't trust it */
+                               if(ctx->allowAnyRoot) {
+                                       sslErrorLog("***Warning: accepting unknown root cert\n");
+                                       break;
+                               }
+                               #if             ST_MANAGES_TRUSTED_ROOTS
+                               if(ctx->newRootCertKc != NULL) {
+                                       /* see if user wants to handle new root */
+                                       serr = sslHandleNewRoot(ctx, &certGroup);
+                               }
+                               else {
+                                       serr = errSSLUnknownRootCert;
+                               }
+                               #else
+                               serr = errSSLUnknownRootCert;
+                               #endif  /* ST_MANAGES_TRUSTED_ROOTS */
+                               break;
+                       case CSSMERR_TP_NOT_TRUSTED:
+                               /* no root, not even in implicit SSL roots */
+                               if(ctx->allowAnyRoot) {
+                                       sslErrorLog("***Warning: accepting unverified cert chain\n");
+                                       break;
+                               }
+                               serr = errSSLNoRootCert;
+                               break;
+                       case CSSMERR_TP_CERT_EXPIRED:
+                               assert(!ctx->allowExpiredCerts);
+                               serr = errSSLCertExpired;
+                               break;
+                       case CSSMERR_TP_CERT_NOT_VALID_YET:
+                               serr = errSSLCertNotYetValid;
+                               break;
+                       default:
+                               stPrintCdsaError(
+                                       "sslVerifyCertChain: CSSM_TP_CertGroupVerify returned",                                         crtn);
+                               serr = errSSLXCertChainInvalid;
+                               break;
+               }
+       }       /* brtn FALSE */
+
+       /* 
+        * don't free individual certs - caller still owns them
+        * don't free struct - on stack 
+        */
+       sslFree(certGroup.GroupList.CertList);
+       return serr;
+}
+
+#if            ST_MANAGES_TRUSTED_ROOTS
+
+/*
+ * Given a DER-encoded cert, obtain its DER-encoded subject name.
+ */
+CSSM_DATA_PTR sslGetCertSubjectName( 
+       SSLContext                      *ctx,
+    const CSSM_DATA_PTR cert)
+{
+       uint32                  NumberOfFields = 0;
+       CSSM_HANDLE     ResultsHandle = 0;
+       CSSM_DATA_PTR   pEncodedName = NULL;
+       CSSM_RETURN             crtn;
+       
+       /* ensure connection to CL */
+       if(attachToCl(ctx)) {
+               return NULL;
+       }
+       crtn = CSSM_CL_CertGetFirstFieldValue(
+               ctx->clHand,
+               cert,
+           &CSSMOID_X509V1SubjectName,
+           &ResultsHandle,
+           &NumberOfFields, 
+               &pEncodedName);
+       if(crtn) {
+               stPrintCdsaError("CertGetFirstFieldValue", crtn);
+       }
+       CSSM_CL_CertAbortQuery(ctx->clHand, ResultsHandle);
+       return pEncodedName;
+}
+#endif /* ST_MANAGES_TRUSTED_ROOTS */
+
+#if            (SSL_DEBUG && ST_MANAGES_TRUSTED_ROOTS)
+void verifyTrustedRoots(SSLContext *ctx,
+       CSSM_DATA_PTR   certs,
+       unsigned                numCerts)
+{      
+       int i;
+       CSSM_DATA_PTR cert;
+       CSSM_BOOL       expired;
+       
+       for(i=0; i<numCerts; i++) {
+               cert = &certs[i];
+               if(!sslVerifyCert(ctx,
+                               cert,
+                               cert,
+                               ctx->cspHand,
+                               &expired)) {
+                       sslErrorLog("Bad trusted cert!\n");
+               }
+       }
+}
+#endif
+
+#ifndef        NDEBUG
+void stPrintCdsaError(const char *op, CSSM_RETURN crtn)
+{
+       cssmPerror(op, crtn);
+}
+
+char *stCssmErrToStr(CSSM_RETURN err)
+{
+       string errStr = cssmErrorString(err);
+       return const_cast<char *>(errStr.c_str());
+}
+#endif
+
+