/* * 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: sslContext.cpp Contains: SSLContext accessors Written by: Doug Mitchell Copyright: (c) 1999 by Apple Computer, Inc., all rights reserved. */ #include "ssl.h" #include "sslContext.h" #include "sslMemory.h" #include #include "sslDigests.h" #include "sslDebug.h" #include "appleCdsa.h" #include "sslKeychain.h" #include "sslUtils.h" #include "cipherSpecs.h" #include "appleSession.h" #include #include #include static void sslFreeDnList( SSLContext *ctx) { DNListElem *dn, *nextDN; dn = ctx->acceptableDNList; while (dn) { SSLFreeBuffer(dn->derDN, ctx); nextDN = dn->next; sslFree(dn); dn = nextDN; } ctx->acceptableDNList = NULL; } static OSStatus sslFreeTrustedRoots( SSLContext *ctx) { unsigned i; assert(ctx != NULL); if((ctx->numTrustedCerts == 0) || (ctx->trustedCerts == NULL)) { /* they really should both be zero, right? */ assert((ctx->numTrustedCerts == 0) && (ctx->trustedCerts == NULL)); } else { for(i=0; inumTrustedCerts; i++) { stFreeCssmData(&ctx->trustedCerts[i], CSSM_FALSE); } sslFree(ctx->trustedCerts); } ctx->numTrustedCerts = 0; ctx->trustedCerts = NULL; sslFreeDnList(ctx); return noErr; } /* * Default attempted version. */ #define DEFAULT_MAX_VERSION TLS_Version_1_0 OSStatus SSLNewContext (Boolean isServer, SSLContextRef *contextPtr) /* RETURNED */ { SSLContext *ctx; OSStatus serr; if(contextPtr == NULL) { return paramErr; } *contextPtr = NULL; ctx = (SSLContext *)sslMalloc(sizeof(SSLContext)); if(ctx == NULL) { return memFullErr; } /* subsequent errors to errOut: */ memset(ctx, 0, sizeof(SSLContext)); ctx->state = SSL_HdskStateUninit; ctx->clientCertState = kSSLClientCertNone; /* different defaults for client and server ... */ if(isServer) { ctx->protocolSide = SSL_ServerSide; ctx->reqProtocolVersion = DEFAULT_MAX_VERSION; } else { ctx->protocolSide = SSL_ClientSide; ctx->reqProtocolVersion = SSL_Version_Undetermined; } ctx->negProtocolVersion = SSL_Version_Undetermined; ctx->maxProtocolVersion = DEFAULT_MAX_VERSION; /* Default value so we can send and receive hello msgs */ ctx->sslTslCalls = &Ssl3Callouts; /* Initialize the cipher state to NULL_WITH_NULL_NULL */ ctx->selectedCipherSpec = &SSL_NULL_WITH_NULL_NULL_CipherSpec; ctx->selectedCipher = ctx->selectedCipherSpec->cipherSpec; ctx->writeCipher.macRef = ctx->selectedCipherSpec->macAlgorithm; ctx->readCipher.macRef = ctx->selectedCipherSpec->macAlgorithm; ctx->readCipher.symCipher = ctx->selectedCipherSpec->cipher; ctx->writeCipher.symCipher = ctx->selectedCipherSpec->cipher; /* these two are invariant */ ctx->writeCipher.encrypting = 1; ctx->writePending.encrypting = 1; /* this gets init'd on first call to SSLHandshake() */ ctx->validCipherSpecs = NULL; ctx->numValidCipherSpecs = 0; ctx->peerDomainName = NULL; ctx->peerDomainNameLen = 0; /* attach to CSP, CL, TP */ serr = attachToAll(ctx); if(serr) { goto errOut; } /* Initial cert verify state: verify with default system roots */ ctx->enableCertVerify = true; /* snag root certs from Keychain, tolerate error */ addBuiltInCerts(ctx); *contextPtr = ctx; return noErr; errOut: sslFree(ctx); return serr; } /* * Dispose of an SSLContext. */ OSStatus SSLDisposeContext (SSLContext *ctx) { WaitingRecord *wait, *next; SSLBuffer buf; if(ctx == NULL) { return paramErr; } sslDeleteCertificateChain(ctx->localCert, ctx); sslDeleteCertificateChain(ctx->encryptCert, ctx); sslDeleteCertificateChain(ctx->peerCert, ctx); ctx->localCert = ctx->encryptCert = ctx->peerCert = NULL; SSLFreeBuffer(ctx->partialReadBuffer, ctx); wait = ctx->recordWriteQueue; while (wait) { SSLFreeBuffer(wait->data, ctx); next = wait->next; buf.data = (uint8*)wait; buf.length = sizeof(WaitingRecord); SSLFreeBuffer(buf, ctx); wait = next; } SSLFreeBuffer(ctx->dhPeerPublic, ctx); SSLFreeBuffer(ctx->dhExchangePublic, ctx); SSLFreeBuffer(ctx->dhPrivate, ctx); CloseHash(SSLHashSHA1, ctx->shaState, ctx); CloseHash(SSLHashMD5, ctx->md5State, ctx); SSLFreeBuffer(ctx->sessionID, ctx); SSLFreeBuffer(ctx->peerID, ctx); SSLFreeBuffer(ctx->resumableSession, ctx); SSLFreeBuffer(ctx->preMasterSecret, ctx); SSLFreeBuffer(ctx->partialReadBuffer, ctx); SSLFreeBuffer(ctx->fragmentedMessageCache, ctx); SSLFreeBuffer(ctx->receivedDataBuffer, ctx); if(ctx->peerDomainName) { sslFree(ctx->peerDomainName); ctx->peerDomainName = NULL; ctx->peerDomainNameLen = 0; } SSLDisposeCipherSuite(&ctx->readCipher, ctx); SSLDisposeCipherSuite(&ctx->writeCipher, ctx); SSLDisposeCipherSuite(&ctx->readPending, ctx); SSLDisposeCipherSuite(&ctx->writePending, ctx); sslFree(ctx->validCipherSpecs); ctx->validCipherSpecs = NULL; ctx->numValidCipherSpecs = 0; /* * NOTE: currently, all public keys come from the CL via CSSM_CL_CertGetKeyInfo. * We really don't know what CSP the CL used to generate a public key (in fact, * it uses the raw CSP only to get LogicalKeySizeInBits, but we can't know * that). Thus using e.g. signingKeyCsp (or any other CSP) to free * signingPubKey is not tecnically accurate. However, our public keys * are all raw keys, and all Apple CSPs dispose of raw keys in the same * way. */ sslFreeKey(ctx->signingKeyCsp, &ctx->signingPubKey, NULL); sslFreeKey(ctx->encryptKeyCsp, &ctx->encryptPubKey, NULL); sslFreeKey(ctx->peerPubKeyCsp, &ctx->peerPubKey, NULL); sslFreeTrustedRoots(ctx); detachFromAll(ctx); memset(ctx, 0, sizeof(SSLContext)); sslFree(ctx); sslCleanupSession(); return noErr; } /* * Determine the state of an SSL session. */ OSStatus SSLGetSessionState (SSLContextRef context, SSLSessionState *state) /* RETURNED */ { SSLSessionState rtnState = kSSLIdle; if(context == NULL) { return paramErr; } *state = rtnState; switch(context->state) { case SSL_HdskStateUninit: case SSL_HdskStateServerUninit: case SSL_HdskStateClientUninit: rtnState = kSSLIdle; break; case SSL_HdskStateGracefulClose: rtnState = kSSLClosed; break; case SSL_HdskStateErrorClose: case SSL_HdskStateNoNotifyClose: rtnState = kSSLAborted; break; case SSL2_HdskStateServerReady: case SSL2_HdskStateClientReady: rtnState = kSSLConnected; break; default: assert((context->state >= SSL_HdskStateServerHello) && (context->state <= SSL2_HdskStateServerFinished)); rtnState = kSSLHandshake; break; } *state = rtnState; return noErr; } OSStatus SSLSetIOFuncs (SSLContextRef ctx, SSLReadFunc read, SSLWriteFunc write) { if(ctx == NULL) { return paramErr; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return badReqErr; } ctx->ioCtx.read = read; ctx->ioCtx.write = write; return noErr; } OSStatus SSLSetConnection (SSLContextRef ctx, SSLConnectionRef connection) { if(ctx == NULL) { return paramErr; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return badReqErr; } ctx->ioCtx.ioRef = connection; return noErr; } OSStatus SSLSetPeerDomainName (SSLContextRef ctx, const char *peerName, size_t peerNameLen) { if(ctx == NULL) { return paramErr; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return badReqErr; } /* free possible existing name */ if(ctx->peerDomainName) { sslFree(ctx->peerDomainName); } /* copy in */ ctx->peerDomainName = (char *)sslMalloc(peerNameLen); if(ctx->peerDomainName == NULL) { return memFullErr; } memmove(ctx->peerDomainName, peerName, peerNameLen); ctx->peerDomainNameLen = peerNameLen; return noErr; } /* * Determine the buffer size needed for SSLGetPeerDomainName(). */ OSStatus SSLGetPeerDomainNameLength (SSLContextRef ctx, size_t *peerNameLen) // RETURNED { if(ctx == NULL) { return paramErr; } *peerNameLen = ctx->peerDomainNameLen; return noErr; } OSStatus SSLGetPeerDomainName (SSLContextRef ctx, char *peerName, // returned here size_t *peerNameLen) // IN/OUT { if(ctx == NULL) { return paramErr; } if(*peerNameLen < ctx->peerDomainNameLen) { return errSSLBufferOverflow; } memmove(peerName, ctx->peerDomainName, ctx->peerDomainNameLen); *peerNameLen = ctx->peerDomainNameLen; return noErr; } OSStatus SSLSetProtocolVersion (SSLContextRef ctx, SSLProtocol version) { SSLProtocolVersion versInt; SSLProtocolVersion versMax; if(ctx == NULL) { return paramErr; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return badReqErr; } /* convert external representation to private */ switch(version) { case kSSLProtocolUnknown: versInt = SSL_Version_Undetermined; versMax = DEFAULT_MAX_VERSION; break; case kSSLProtocol2: versInt = versMax = SSL_Version_2_0; break; case kSSLProtocol3: /* this tells us to do our best but allows 2.0 */ versInt = SSL_Version_Undetermined; versMax = SSL_Version_3_0; break; case kSSLProtocol3Only: versInt = SSL_Version_3_0_Only; versMax = SSL_Version_3_0; break; case kTLSProtocol1: /* this tells us to do our best but allows 2.0 */ versInt = SSL_Version_Undetermined; versMax = TLS_Version_1_0; break; case kTLSProtocol1Only: versInt = TLS_Version_1_0_Only; versMax = TLS_Version_1_0; break; default: return paramErr; } ctx->reqProtocolVersion = ctx->negProtocolVersion = versInt; ctx->maxProtocolVersion = versMax; return noErr; } static SSLProtocol convertProtToExtern(SSLProtocolVersion prot) { switch(prot) { case SSL_Version_Undetermined: return kSSLProtocolUnknown; case SSL_Version_3_0_Only: return kSSLProtocol3Only; case SSL_Version_2_0: return kSSLProtocol2; case SSL_Version_3_0: return kSSLProtocol3; case TLS_Version_1_0_Only: return kTLSProtocol1Only; case TLS_Version_1_0: return kTLSProtocol1; /* this can happen in an intermediate state while negotiation * is active...right? */ case SSL_Version_3_0_With_2_0_Hello: return kSSLProtocolUnknown; default: sslErrorLog("convertProtToExtern: bad prot\n"); return kSSLProtocolUnknown; } /* not reached but make compiler happy */ return kSSLProtocolUnknown; } OSStatus SSLGetProtocolVersion (SSLContextRef ctx, SSLProtocol *protocol) /* RETURNED */ { if(ctx == NULL) { return paramErr; } *protocol = convertProtToExtern(ctx->reqProtocolVersion); return noErr; } OSStatus SSLGetNegotiatedProtocolVersion (SSLContextRef ctx, SSLProtocol *protocol) /* RETURNED */ { if(ctx == NULL) { return paramErr; } *protocol = convertProtToExtern(ctx->negProtocolVersion); return noErr; } OSStatus SSLSetEnableCertVerify (SSLContextRef ctx, Boolean enableVerify) { if(ctx == NULL) { return paramErr; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return badReqErr; } ctx->enableCertVerify = enableVerify; return noErr; } OSStatus SSLGetEnableCertVerify (SSLContextRef ctx, Boolean *enableVerify) { if(ctx == NULL) { return paramErr; } *enableVerify = ctx->enableCertVerify; return noErr; } OSStatus SSLSetAllowsExpiredCerts(SSLContextRef ctx, Boolean allowExpired) { if(ctx == NULL) { return paramErr; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return badReqErr; } ctx->allowExpiredCerts = allowExpired; return noErr; } OSStatus SSLGetAllowsExpiredCerts (SSLContextRef ctx, Boolean *allowExpired) { if(ctx == NULL) { return paramErr; } *allowExpired = ctx->allowExpiredCerts; return noErr; } OSStatus SSLSetAllowsExpiredRoots(SSLContextRef ctx, Boolean allowExpired) { if(ctx == NULL) { return paramErr; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return badReqErr; } ctx->allowExpiredRoots = allowExpired; return noErr; } OSStatus SSLGetAllowsExpiredRoots (SSLContextRef ctx, Boolean *allowExpired) { if(ctx == NULL) { return paramErr; } *allowExpired = ctx->allowExpiredRoots; return noErr; } OSStatus SSLSetAllowsAnyRoot( SSLContextRef ctx, Boolean anyRoot) { if(ctx == NULL) { return paramErr; } ctx->allowAnyRoot = anyRoot; return noErr; } OSStatus SSLGetAllowsAnyRoot( SSLContextRef ctx, Boolean *anyRoot) { if(ctx == NULL) { return paramErr; } *anyRoot = ctx->allowAnyRoot; return noErr; } OSStatus SSLSetTrustedRoots (SSLContextRef ctx, CFArrayRef trustedRoots, Boolean replaceExisting) { unsigned dex; unsigned outDex; unsigned numIncoming; uint32 numCerts; CSSM_DATA_PTR newRoots = NULL; const CSSM_DATA *existAnchors = NULL; uint32 numExistAnchors = 0; OSStatus ortn = noErr; if(ctx == NULL) { return paramErr; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return badReqErr; } numCerts = numIncoming = CFArrayGetCount(trustedRoots); if(!replaceExisting) { if(ctx->trustedCerts != NULL) { /* adding to existing store */ existAnchors = ctx->trustedCerts; numExistAnchors = ctx->numTrustedCerts; } else { /* adding to system roots */ ortn = SecTrustGetCSSMAnchorCertificates(&existAnchors, &numExistAnchors); if(ortn) { /* should never happen */ return ortn; } } numCerts += numExistAnchors; } newRoots = (CSSM_DATA_PTR)sslMalloc(numCerts * sizeof(CSSM_DATA)); memset(newRoots, 0, numCerts * sizeof(CSSM_DATA)); /* Caller's certs first */ for(dex=0, outDex=0; dexnumTrustedCerts = numCerts; ctx->trustedCerts = newRoots; return noErr; abort: sslFree(newRoots); return ortn; } OSStatus SSLGetTrustedRoots (SSLContextRef ctx, CFArrayRef *trustedRoots) /* RETURNED */ { uint32 numCerts; const CSSM_DATA *certs; CFMutableArrayRef certArray; unsigned dex; SecCertificateRef secCert; OSStatus ortn; if(ctx == NULL) { return paramErr; } if(ctx->trustedCerts != NULL) { /* use ours */ certs = ctx->trustedCerts; numCerts = ctx->numTrustedCerts; } else { /* use default system roots */ OSStatus ortn = SecTrustGetCSSMAnchorCertificates(&certs, &numCerts); if(ortn) { /* should never happen */ return ortn; } } certArray = CFArrayCreateMutable(kCFAllocatorDefault, (CFIndex)numCerts, &kCFTypeArrayCallBacks); if(certArray == NULL) { return memFullErr; } for(dex=0; dexclientAuth = auth; switch(auth) { case kNeverAuthenticate: ctx->tryClientAuth = false; break; case kAlwaysAuthenticate: case kTryAuthenticate: ctx->tryClientAuth = true; break; } return noErr; } OSStatus SSLGetClientCertificateState (SSLContextRef ctx, SSLClientCertificateState *clientState) { if(ctx == NULL) { return paramErr; } *clientState = ctx->clientCertState; return noErr; } OSStatus SSLSetCertificate (SSLContextRef ctx, CFArrayRef certRefs) { /* * -- free localCerts if we have any * -- Get raw cert data, convert to ctx->localCert * -- get pub, priv keys from certRef[0] * -- validate cert chain */ if(ctx == NULL) { return paramErr; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return badReqErr; } return parseIncomingCerts(ctx, certRefs, &ctx->localCert, &ctx->signingPubKey, &ctx->signingPrivKey, &ctx->signingKeyCsp #if ST_KC_KEYS_NEED_REF , &ctx->signingKeyRef #else ); #endif } OSStatus SSLSetEncryptionCertificate (SSLContextRef ctx, CFArrayRef certRefs) { /* * -- free encryptCert if we have any * -- Get raw cert data, convert to ctx->encryptCert * -- get pub, priv keys from certRef[0] * -- validate cert chain */ if(ctx == NULL) { return paramErr; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return badReqErr; } return parseIncomingCerts(ctx, certRefs, &ctx->encryptCert, &ctx->encryptPubKey, &ctx->encryptPrivKey, &ctx->encryptKeyCsp #if ST_KC_KEYS_NEED_REF , &ctx->encryptKeyRef); #else ); #endif } #if ST_MANAGES_TRUSTED_ROOTS /* * Add (optional, additional) trusted root certs. */ OSStatus SSLSetTrustedRootCertKC (SSLContextRef ctx, KCRef keyChainRef, Boolean deleteExisting) { /* * -- free trustedCerts if deleteExisting * -- Get raw cert data, add to ctx->trustedCerts * -- verify that each of these is a valid (self-verifying) * root cert * -- add each subject name to acceptableDNList */ if((ctx == NULL) || (keyChainRef == nil)) { return paramErr; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return badReqErr; } if(deleteExisting) { sslFreeTrustedRoots(ctx); } return parseTrustedKeychain(ctx, keyChainRef); } OSStatus SSLSetNewRootKC (SSLContextRef ctx, KCRef keyChainRef, void *accessCreds) { if((ctx == NULL) || (keyChainRef == nil)) { return paramErr; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return badReqErr; } if(ctx->newRootCertKc != NULL) { /* can't do this multiple times */ return badReqErr; } ctx->newRootCertKc = keyChainRef; ctx->accessCreds = accessCreds; return noErr; } #endif /* ST_MANAGES_TRUSTED_ROOTS */ OSStatus SSLSetPeerID (SSLContext *ctx, const void *peerID, size_t peerIDLen) { OSStatus serr; /* copy peerId to context->peerId */ if((ctx == NULL) || (peerID == NULL) || (peerIDLen == 0)) { return paramErr; } if(sslIsSessionActive(ctx)) { /* can't do this with an active session */ return badReqErr; } SSLFreeBuffer(ctx->peerID, ctx); serr = SSLAllocBuffer(ctx->peerID, peerIDLen, ctx); if(serr) { return serr; } memmove(ctx->peerID.data, peerID, peerIDLen); return noErr; } OSStatus SSLGetPeerID (SSLContextRef ctx, const void **peerID, size_t *peerIDLen) { *peerID = ctx->peerID.data; // may be NULL *peerIDLen = ctx->peerID.length; return noErr; } OSStatus SSLGetNegotiatedCipher (SSLContextRef ctx, SSLCipherSuite *cipherSuite) { if(ctx == NULL) { return paramErr; } if(!sslIsSessionActive(ctx)) { return badReqErr; } *cipherSuite = (SSLCipherSuite)ctx->selectedCipher; return noErr; } /* * Add an acceptable distinguished name (client authentication only). */ OSStatus SSLAddDistinguishedName( SSLContextRef ctx, const void *derDN, size_t derDNLen) { DNListElem *dn; OSStatus err; dn = (DNListElem *)sslMalloc(sizeof(DNListElem)); if(dn == NULL) { return memFullErr; } if ((err = SSLAllocBuffer(dn->derDN, derDNLen, ctx)) != 0) return err; memcpy(dn->derDN.data, derDN, derDNLen); dn->next = ctx->acceptableDNList; ctx->acceptableDNList = dn; return noErr; } /* * Request peer certificates. Valid anytime, subsequent to * a handshake attempt. */ OSStatus SSLGetPeerCertificates (SSLContextRef ctx, CFArrayRef *certs) { uint32 numCerts; CFMutableArrayRef ca; CFIndex i; SecCertificateRef cfd; OSStatus ortn; CSSM_DATA certData; SSLCertificate *scert; if(ctx == NULL) { return paramErr; } *certs = NULL; /* * Copy peerCert, a chain of SSLCertificates, to a CFArray of * CFDataRefs, each of which is one DER-encoded cert. */ numCerts = SSLGetCertificateChainLength(ctx->peerCert); if(numCerts == 0) { return noErr; } ca = CFArrayCreateMutable(kCFAllocatorDefault, (CFIndex)numCerts, &kCFTypeArrayCallBacks); if(ca == NULL) { return memFullErr; } /* * Caller gets leaf cert first, the opposite of the way we store them. */ scert = ctx->peerCert; for(i=0; (unsigned)iderCert, &certData); ortn = SecCertificateCreateFromData(&certData, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &cfd); if(ortn) { CFRelease(ca); return ortn; } /* insert at head of array */ CFArrayInsertValueAtIndex(ca, 0, cfd); scert = scert->next; } *certs = ca; return noErr; } OSStatus SSLInternalMasterSecret( SSLContextRef ctx, void *secret, // mallocd by caller, SSL_MASTER_SECRET_SIZE size_t *secretSize) // in/out { if((ctx == NULL) || (secret == NULL) || (secretSize == NULL)) { return paramErr; } if(*secretSize < SSL_MASTER_SECRET_SIZE) { return paramErr; } memmove(secret, ctx->masterSecret, SSL_MASTER_SECRET_SIZE); *secretSize = SSL_MASTER_SECRET_SIZE; return noErr; } OSStatus SSLInternalServerRandom( SSLContextRef ctx, void *rand, // mallocd by caller, SSL_CLIENT_SRVR_RAND_SIZE size_t *randSize) // in/out { if((ctx == NULL) || (rand == NULL) || (randSize == NULL)) { return paramErr; } if(*randSize < SSL_CLIENT_SRVR_RAND_SIZE) { return paramErr; } memmove(rand, ctx->serverRandom, SSL_CLIENT_SRVR_RAND_SIZE); *randSize = SSL_CLIENT_SRVR_RAND_SIZE; return noErr; } OSStatus SSLInternalClientRandom( SSLContextRef ctx, void *rand, // mallocd by caller, SSL_CLIENT_SRVR_RAND_SIZE size_t *randSize) // in/out { if((ctx == NULL) || (rand == NULL) || (randSize == NULL)) { return paramErr; } if(*randSize < SSL_CLIENT_SRVR_RAND_SIZE) { return paramErr; } memmove(rand, ctx->clientRandom, SSL_CLIENT_SRVR_RAND_SIZE); *randSize = SSL_CLIENT_SRVR_RAND_SIZE; return noErr; }