]> git.saurik.com Git - apple/security.git/blobdiff - SecureTransport/ssl2Message.cpp
Security-54.1.3.tar.gz
[apple/security.git] / SecureTransport / ssl2Message.cpp
diff --git a/SecureTransport/ssl2Message.cpp b/SecureTransport/ssl2Message.cpp
new file mode 100644 (file)
index 0000000..756a40f
--- /dev/null
@@ -0,0 +1,779 @@
+/*
+ * 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.
+ */
+
+#include "ssl.h"
+#include "ssl2.h"
+#include "sslRecord.h"
+#include "sslMemory.h"
+#include "sslContext.h"
+#include "sslAlertMessage.h"
+#include "sslHandshake.h"
+#include "sslSession.h"
+#include "sslDebug.h"
+#include "cipherSpecs.h"
+#include "appleCdsa.h"
+#include "sslUtils.h"
+
+#include <string.h>
+#include <assert.h>
+
+OSStatus
+SSL2ProcessClientHello(SSLBuffer msg, SSLContext *ctx)
+{   OSStatus                   err;
+    UInt8               *charPtr, *cipherList;
+    unsigned            i, j, cipherKindCount, sessionIDLen, challengeLen;
+    SSL2CipherKind      cipherKind;
+    SSLCipherSuite      matchingCipher, selectedCipher;
+    SSLProtocolVersion  version;
+    
+    if (msg.length < 27) {
+               sslErrorLog("SSL2ProcessClientHello: msg len error 1\n");
+        return errSSLProtocol;
+    }
+    
+    charPtr = msg.data;
+    
+    version = (SSLProtocolVersion)SSLDecodeInt(charPtr, 2);
+       if (version > ctx->maxProtocolVersion) {
+               version = ctx->maxProtocolVersion;
+       }
+    /* FIXME - I think this needs work for a SSL_Version_2_0 server, to ensure that
+        * the client isn't establishing a v3 session. */
+    if (ctx->negProtocolVersion == SSL_Version_Undetermined)
+    {   
+               /* FIXME - this ifndef should not be necessary */
+               #ifndef NDEBUG
+        sslLogNegotiateDebug("===SSL2 server: negVersion was undetermined; "
+                       "is %s", protocolVersStr(version));
+               #endif
+        ctx->negProtocolVersion = version;
+               if(version >= TLS_Version_1_0) {
+                       ctx->sslTslCalls = &Tls1Callouts;
+               }
+               else {
+                       /* default from context init */
+                       assert(ctx->sslTslCalls == &Ssl3Callouts);
+               }
+    }
+    else if (ctx->negProtocolVersion == SSL_Version_3_0_With_2_0_Hello)
+    {   if (version < SSL_Version_3_0) {
+                       sslErrorLog("SSL2ProcessClientHello: version error\n");
+            return errSSLProtocol;
+        }
+               /* FIXME - I don't think path is ever taken - we NEVER set any
+                * protocol var to      SSL_Version_3_0_With_2_0_Hello... */
+        sslLogNegotiateDebug("===SSL2 server: negVersion was "
+                       "3_0_With_2_0_Hello; is 3_0");
+        ctx->negProtocolVersion = version;
+    }
+    
+    charPtr += 2;
+    cipherKindCount = SSLDecodeInt(charPtr, 2);
+    charPtr += 2;
+    if (cipherKindCount % 3 != 0) {
+               sslErrorLog("SSL2ProcessClientHello: cipherKindCount error\n");
+        return errSSLProtocol;
+    }
+    cipherKindCount /= 3;
+    sessionIDLen = SSLDecodeInt(charPtr, 2);
+    charPtr += 2;
+    challengeLen = SSLDecodeInt(charPtr, 2);
+    charPtr += 2;
+    
+    if (msg.length != 8 + 3*cipherKindCount + sessionIDLen + challengeLen ||
+        (sessionIDLen != 0 && sessionIDLen != 16) ||
+        challengeLen < 16 || challengeLen > 32 ) {
+               sslErrorLog("SSL2ProcessClientHello: msg len error 2\n");
+        return errSSLProtocol;
+    }
+    cipherList = charPtr;
+    selectedCipher = SSL_NO_SUCH_CIPHERSUITE;
+
+    if (ctx->negProtocolVersion >= SSL_Version_3_0) {
+               /* If we're negotiating an SSL 3.0 session, use SSL 3.0 suites first */
+        for (i = 0; i < cipherKindCount; i++) {
+            cipherKind = (SSL2CipherKind)SSLDecodeInt(charPtr, 3);
+            charPtr += 3;
+            if (selectedCipher != SSL_NO_SUCH_CIPHERSUITE)
+                continue;
+            if ((((UInt32)cipherKind) & 0xFF0000) != 0)
+                continue;       /* Skip SSL 2 suites */
+            matchingCipher = (SSLCipherSuite)((UInt32)cipherKind & 0x00FFFF);
+            for (j = 0; j<ctx->numValidCipherSpecs; j++) {
+                if (ctx->validCipherSpecs[j].cipherSpec == matchingCipher) {
+                    selectedCipher = matchingCipher;
+                    break;
+                }
+                       }       /* searching thru all our valid ciphers */
+        }      /* for each client cipher */
+    }  /* v3 or greater */
+    
+       if(selectedCipher == SSL_NO_SUCH_CIPHERSUITE) {
+               /* try again using SSL2 ciphers only */
+           charPtr = cipherList;
+               for (i = 0; i < cipherKindCount; i++) {
+                       cipherKind = (SSL2CipherKind)SSLDecodeInt(charPtr, 3);
+                       charPtr += 3;
+                       if (selectedCipher == SSL_NO_SUCH_CIPHERSUITE) {
+                               /* After we find one, just keep advancing ptr past 
+                                * the unused ones */
+                               if ((((UInt32)cipherKind) & 0xFF0000) != 0) {
+                                       /* If it's a real SSL2 spec, look for it in the list */
+                                       matchingCipher = SSL_NO_SUCH_CIPHERSUITE;
+                                       for (j = 0; j < SSL2CipherMapCount; j++) {
+                                               if (cipherKind == SSL2CipherMap[j].cipherKind) {
+                                                       matchingCipher = SSL2CipherMap[j].cipherSuite;
+                                                       break;
+                                               }
+                                       }
+                               }       /* real 3-byte SSL2 suite */
+                               else {
+                                       /* if the first byte is zero, it's an encoded SSL 3 CipherSuite */
+                                       matchingCipher = (SSLCipherSuite)((UInt32)cipherKind & 0x00FFFF);
+                                       /* 
+                                       * One more restriction - if we've negotiated a v2 session,
+                                       * ignore this matching cipher if it's not in the SSL2 map.
+                                       */
+                                       if(ctx->negProtocolVersion < SSL_Version_3_0) {
+                                               int isInMap = 0;
+                                               for (j = 0; j < SSL2CipherMapCount; j++) {
+                                                       if (matchingCipher == SSL2CipherMap[j].cipherSuite) {
+                                                               isInMap = 1;
+                                                               break;
+                                                       }
+                                               }
+                                               if(!isInMap) {
+                                                       /* Sorry, no can do */
+                                                       matchingCipher = SSL_NO_SUCH_CIPHERSUITE;
+                                               }
+                                       }       /* SSL2 check */
+                               }       /* two-byte suite */
+                               
+                               /* now see if we are enabled for this cipher */
+                               if (matchingCipher != SSL_NO_SUCH_CIPHERSUITE) {
+                                       for (j = 0; j < ctx->numValidCipherSpecs; j++) {
+                                               if (ctx->validCipherSpecs[j].cipherSpec == matchingCipher) {
+                                                       selectedCipher = matchingCipher;
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }       /* not ignoring this suite */
+               }       /* for each suite in the hello msg */
+       }               /* not found in SSL3 ciphersuites */
+       
+    if (selectedCipher == SSL_NO_SUCH_CIPHERSUITE)
+        return errSSLNegotiation;
+    
+    ctx->selectedCipher = selectedCipher;
+    err = FindCipherSpec(ctx);
+    if(err != 0) {
+        return err;
+    }
+    if (sessionIDLen > 0 && ctx->peerID.data != 0)
+    {   /* Don't die on error; just treat it as an uncacheable session */
+        err = SSLAllocBuffer(ctx->sessionID, sessionIDLen, ctx);
+        if (err == 0)
+            memcpy(ctx->sessionID.data, charPtr, sessionIDLen);
+    }
+    charPtr += sessionIDLen;
+    
+    ctx->ssl2ChallengeLength = challengeLen;
+    memset(ctx->clientRandom, 0, SSL_CLIENT_SRVR_RAND_SIZE);
+    memcpy(ctx->clientRandom + SSL_CLIENT_SRVR_RAND_SIZE - challengeLen, 
+               charPtr, challengeLen);
+    charPtr += challengeLen;
+    assert(charPtr == msg.data + msg.length);
+    
+    return noErr;
+}
+
+/*
+ * The SSL v2 spec says that the challenge string sent by the client can be
+ * between 16 and 32 bytes. However all Netscape enterprise servers actually
+ * require a 16 byte challenge. Q.v. cdnow.com, store.apple.com. 
+ * Unfortunately this means that when we're trying to do a 
+ * SSL_Version_3_0_With_2_0_Hello negotiation, we have to limit ourself to 
+ * a 16-byte clientRandom, which we have to concatenate to 16 bytes of 
+ * zeroes if we end up with a 3.0 or 3.1 connection. Thus we lose 16 bytes
+ * of entropy.
+ */
+#define SSL2_CHALLENGE_LEN     16
+
+OSStatus
+SSL2EncodeClientHello(SSLBuffer &msg, SSLContext *ctx)
+{   OSStatus   err;
+    UInt8              *charPtr;
+    unsigned           i, j;
+       int                     useSSL3Ciphers = 0;
+       int                     totalCipherCount;
+    int                        sessionIDLen;
+    UInt16             version;
+    SSLBuffer  sessionIdentifier, randomData;
+    
+    switch (ctx->negProtocolVersion)
+    {   case SSL_Version_Undetermined:
+        case SSL_Version_3_0_With_2_0_Hello:
+               /* go for it, see if server can handle upgrading */
+               useSSL3Ciphers = 1;
+                       /* could be SSLv3 or TLSv1 */
+            version = ctx->maxProtocolVersion;
+            break;
+        case SSL_Version_2_0:
+            useSSL3Ciphers = 0;
+            version = SSL_Version_2_0;
+            break;
+        case SSL_Version_3_0_Only:
+        case SSL_Version_3_0:
+        case TLS_Version_1_0_Only:
+        case TLS_Version_1_0:
+        default:
+            assert("Bad protocol version for sending SSL 2 Client Hello");
+            return errSSLInternal;
+    }
+       /* FIXME - this ifndef should not be necessary */
+       #ifndef NDEBUG
+       sslLogNegotiateDebug("===SSL client: proclaiming %s capable", 
+               protocolVersStr((SSLProtocolVersion)version));
+    #endif
+       
+    if (useSSL3Ciphers != 0)
+        totalCipherCount = ctx->numValidCipherSpecs;
+    else
+        totalCipherCount = 0;
+        
+    for (i = 0; i < SSL2CipherMapCount; i++)
+        for (j = 0; j < ctx->numValidCipherSpecs; j++)
+            if (ctx->validCipherSpecs[j].cipherSpec == SSL2CipherMap[i].cipherSuite)
+            {   totalCipherCount++;
+                break;
+            }
+    
+    sessionIDLen = 0;
+    sessionIdentifier.data = 0;
+    if (ctx->resumableSession.data != 0)
+    {   if ((err = SSLRetrieveSessionID(ctx->resumableSession, &sessionIdentifier, ctx)) != 0)
+            return err;
+        sessionIDLen = sessionIdentifier.length;
+    }
+    
+       /* msg length = 9 + 3 * totalCipherCount + sessionIDLen + 16 bytes of challenge
+        *  Use exactly 16 bytes of challenge because Netscape products have a bug
+        *  that requires this length
+        */ 
+    if ((err = SSLAllocBuffer(msg, 9 + (3*totalCipherCount) + sessionIDLen + 
+                       SSL2_CHALLENGE_LEN, ctx)) != 0)
+    {   SSLFreeBuffer(sessionIdentifier, ctx);
+        return err;
+    }
+    
+    charPtr = msg.data;
+    *charPtr++ = SSL2_MsgClientHello;
+    charPtr = SSLEncodeInt(charPtr, version, 2);
+    charPtr = SSLEncodeInt(charPtr, 3*totalCipherCount, 2);
+    charPtr = SSLEncodeInt(charPtr, sessionIDLen, 2);
+    charPtr = SSLEncodeInt(charPtr, SSL2_CHALLENGE_LEN, 2);
+    
+       /* If we can send SSL3 ciphers, encode the two-byte cipher specs into three-byte
+        *  CipherKinds which have a leading 0.
+        */
+    if (useSSL3Ciphers != 0)
+        for (i = 0; i < ctx->numValidCipherSpecs; i++)
+            charPtr = SSLEncodeInt(charPtr, ctx->validCipherSpecs[i].cipherSpec, 3);
+    
+       /* Now send those SSL2 specs for which we have implementations */
+    for (i = 0; i < SSL2CipherMapCount; i++)
+        for (j = 0; j < ctx->numValidCipherSpecs; j++)
+            if (ctx->validCipherSpecs[j].cipherSpec == SSL2CipherMap[i].cipherSuite)
+            {   charPtr = SSLEncodeInt(charPtr, SSL2CipherMap[i].cipherKind, 3);
+                break;
+            }
+    
+    if (sessionIDLen > 0)
+    {   memcpy(charPtr, sessionIdentifier.data, sessionIDLen);
+        charPtr += sessionIDLen;
+        SSLFreeBuffer(sessionIdentifier, ctx);
+    }
+    
+    randomData.data = charPtr;
+    randomData.length = SSL2_CHALLENGE_LEN;
+    if ((err = sslRand(ctx, &randomData)) != 0)
+    {   SSLFreeBuffer(msg, ctx);
+        return err;
+    }
+    charPtr += SSL2_CHALLENGE_LEN;
+    
+       /* Zero out the first 16 bytes of clientRandom, and store 
+        * the challenge in the second 16 bytes */
+       #if (SSL2_CHALLENGE_LEN == SSL_CLIENT_SRVR_RAND_SIZE)
+       /* this path verified to fail with Netscape Enterprise servers 1/16/02 */
+    memcpy(ctx->clientRandom, randomData.data, SSL2_CHALLENGE_LEN);
+       #else
+    memset(ctx->clientRandom, 0, SSL_CLIENT_SRVR_RAND_SIZE - SSL2_CHALLENGE_LEN);
+    memcpy(ctx->clientRandom + SSL_CLIENT_SRVR_RAND_SIZE - SSL2_CHALLENGE_LEN, 
+                       randomData.data, SSL2_CHALLENGE_LEN);
+       #endif
+    ctx->ssl2ChallengeLength = SSL2_CHALLENGE_LEN;
+    
+    assert(charPtr == msg.data + msg.length);
+    
+    return noErr;
+}
+
+OSStatus
+SSL2ProcessClientMasterKey(SSLBuffer msg, SSLContext *ctx)
+{   OSStatus        err;
+    SSL2CipherKind  cipherKind;
+    SSLBuffer       secretData;
+    unsigned        clearLength, encryptedLength, keyArgLength;
+    UInt32             secretLength, localKeyModulusLen;
+    UInt8           *charPtr;
+    const CSSM_KEY     *decryptKey;
+       CSSM_CSP_HANDLE decryptCspHand;
+       
+    if (msg.length < 9) {
+               sslErrorLog("SSL2ProcessClientMasterKey: msg.length error 1\n");
+        return errSSLProtocol;
+    }
+    assert(ctx->protocolSide == SSL_ServerSide);
+    
+    charPtr = msg.data;
+    cipherKind = (SSL2CipherKind)SSLDecodeInt(charPtr, 3);
+    charPtr += 3;
+    clearLength = SSLDecodeInt(charPtr, 2);
+    charPtr += 2;
+    encryptedLength = SSLDecodeInt(charPtr, 2);
+    charPtr += 2;
+    keyArgLength = SSLDecodeInt(charPtr, 2);
+    charPtr += 2;
+    
+    if (msg.length != 9 + clearLength + encryptedLength + keyArgLength) {
+               sslErrorLog("SSL2ProcessClientMasterKey: msg.length error 2\n");
+        return errSSLProtocol;
+    }
+    
+       /* Master key == CLEAR_DATA || SECRET_DATA */
+    memcpy(ctx->masterSecret, charPtr, clearLength);
+    charPtr += clearLength;
+
+       /* 
+        * Just as in SSL2EncodeServerHello, which key we use depends on the
+        * app's config.
+        */ 
+       if(ctx->encryptPrivKey) {
+               decryptKey = ctx->encryptPrivKey;
+               assert(ctx->encryptKeyCsp != 0);
+               decryptCspHand = ctx->encryptKeyCsp;
+       }
+       else if(ctx->signingPrivKey) {
+               decryptKey = ctx->signingPrivKey;
+               assert(ctx->signingKeyCsp != 0);
+               decryptCspHand = ctx->signingKeyCsp;
+       }
+       else {
+               /* really should not happen... */
+               sslErrorLog("SSL2ProcessClientMasterKey: No server key!\n");
+               return badReqErr;
+       }
+       localKeyModulusLen = sslKeyLengthInBytes(decryptKey);
+
+    if (encryptedLength != localKeyModulusLen) {
+               sslErrorLog("SSL2ProcessClientMasterKey: encryptedLength error 1\n");
+        return errSSLProtocol;
+       }
+       
+       /* Allocate enough room to hold any decrypted value */
+    if ((err = SSLAllocBuffer(secretData, encryptedLength, ctx)) != 0)
+        return err;
+    
+       err = sslRsaDecrypt(ctx,
+               decryptKey,
+               decryptCspHand,
+               charPtr, 
+               encryptedLength,
+               secretData.data,
+               encryptedLength,        // same length for both...? 
+               &secretLength);
+       if(err) {
+               SSLFreeBuffer(secretData, ctx);
+               return err;
+       }
+    
+    charPtr += encryptedLength;
+    
+    if (clearLength + secretLength != ctx->selectedCipherSpec->cipher->keySize) {
+       sslErrorLog("SSL2ProcessClientMasterKey: length error 3\n");
+        return errSSLProtocol;
+    }
+    memcpy(ctx->masterSecret + clearLength, secretData.data, secretLength);
+    if ((err = SSLFreeBuffer(secretData, ctx)) != 0)
+        return err;
+    
+    if (keyArgLength != ctx->selectedCipherSpec->cipher->ivSize) {
+       sslErrorLog("SSL2ProcessClientMasterKey: length error 4\n");
+        return errSSLProtocol;
+    }
+    
+       /* Stash the IV after the master key in master secret storage */
+    memcpy(ctx->masterSecret + ctx->selectedCipherSpec->cipher->keySize, charPtr, keyArgLength);
+    charPtr += keyArgLength;
+    assert(charPtr = msg.data + msg.length);
+    
+    return noErr;
+}
+
+OSStatus
+SSL2EncodeClientMasterKey(SSLBuffer &msg, SSLContext *ctx)
+{   OSStatus            err;
+    unsigned            length, i, clearLen;
+    UInt32                     outputLen, peerKeyModulusLen;
+    SSLBuffer           keyData;
+    UInt8               *charPtr;
+       
+       peerKeyModulusLen = sslKeyLengthInBytes(ctx->peerPubKey);
+
+       /* Length is 10 + clear key size + encrypted output size + iv size */
+    length = 10;
+    clearLen = ctx->selectedCipherSpec->cipher->keySize - ctx->selectedCipherSpec->cipher->secretKeySize;
+    length += clearLen;
+    length += peerKeyModulusLen;
+    length += ctx->selectedCipherSpec->cipher->ivSize;
+    
+    if ((err = SSLAllocBuffer(msg, length, ctx)) != 0)
+        return err;
+    charPtr = msg.data;
+    *charPtr++ = SSL2_MsgClientMasterKey;
+    for (i = 0; i < SSL2CipherMapCount; i++)
+        if (ctx->selectedCipher == SSL2CipherMap[i].cipherSuite)
+            break;
+    assert(i < SSL2CipherMapCount);
+       sslLogNegotiateDebug("===SSL2EncodeClientMasterKey: sending cipherKind 0x%x", 
+               SSL2CipherMap[i].cipherKind);
+    charPtr = SSLEncodeInt(charPtr, SSL2CipherMap[i].cipherKind, 3);
+    charPtr = SSLEncodeInt(charPtr, clearLen, 2);
+    charPtr = SSLEncodeInt(charPtr, peerKeyModulusLen, 2);
+    charPtr = SSLEncodeInt(charPtr, ctx->selectedCipherSpec->cipher->ivSize, 2);
+    
+    /* Generate the keying material; we need enough data for the key and IV */
+    keyData.data = ctx->masterSecret;
+    keyData.length = ctx->selectedCipherSpec->cipher->keySize + ctx->selectedCipherSpec->cipher->ivSize;
+    assert(keyData.length <= 48);   /* Must be able to store it in the masterSecret array */
+    if ((err = sslRand(ctx, &keyData)) != 0)
+        return err;
+    
+    memcpy(charPtr, ctx->masterSecret, clearLen);
+    charPtr += clearLen;
+    
+       /* Replace this with code to do encryption at lower level & set PKCS1 padding
+    for rollback attack */
+
+       /* 
+        * encrypt only the secret key portion of masterSecret, starting at
+        * clearLen bytes
+        */
+       err = sslRsaEncrypt(ctx,
+               ctx->peerPubKey,
+               ctx->peerPubKeyCsp,             // XX - maybe cspHand
+               ctx->masterSecret + clearLen,
+               ctx->selectedCipherSpec->cipher->keySize - clearLen,
+               charPtr, 
+               peerKeyModulusLen,
+               &outputLen);
+       if(err) {
+               return err;
+       }
+
+    charPtr += outputLen;
+        
+    /* copy clear IV to msg buf */
+    memcpy(charPtr, ctx->masterSecret + ctx->selectedCipherSpec->cipher->keySize,
+            ctx->selectedCipherSpec->cipher->ivSize);
+    charPtr += ctx->selectedCipherSpec->cipher->ivSize;
+    
+    assert(charPtr == msg.data + msg.length);
+    
+    return noErr;
+}
+
+OSStatus
+SSL2ProcessClientFinished(SSLBuffer msg, SSLContext *ctx)
+{   if (msg.length != ctx->sessionID.length) {
+       sslErrorLog("SSL2ProcessClientFinished: length error\n");
+        return errSSLProtocol;
+    }
+    if (memcmp(msg.data, ctx->serverRandom, ctx->ssl2ConnectionIDLength) != 0) {
+       sslErrorLog("SSL2ProcessClientFinished: data compare error\n");
+        return errSSLProtocol;
+       }    
+    return noErr;
+}
+
+OSStatus
+SSL2EncodeClientFinished(SSLBuffer &msg, SSLContext *ctx)
+{   OSStatus      err;
+    
+    if ((err = SSLAllocBuffer(msg, ctx->ssl2ConnectionIDLength+1, ctx)) != 0)
+        return err;
+    msg.data[0] = SSL2_MsgClientFinished;
+    memcpy(msg.data+1, ctx->serverRandom, ctx->ssl2ConnectionIDLength);
+    return noErr;
+}
+
+OSStatus
+SSL2ProcessServerHello(SSLBuffer msg, SSLContext *ctx)
+{   OSStatus            err;
+    SSL2CertTypeCode    certType;
+    unsigned            sessionIDMatch, certLen, cipherSpecsLen, connectionIDLen;
+    unsigned            i, j;
+    SSL2CipherKind      cipherKind;
+    SSLCertificate      *cert;
+    SSLCipherSuite      matchingCipher = 0;            // avoid compiler warning
+    SSLCipherSuite      selectedCipher;
+    UInt8               *charPtr;
+    SSLProtocolVersion  version;
+    
+    if (msg.length < 10) {
+       sslErrorLog("SSL2ProcessServerHello: length error\n");
+        return errSSLProtocol;
+    }
+    charPtr = msg.data;
+    
+    sessionIDMatch = *charPtr++;
+    certType = (SSL2CertTypeCode)*charPtr++;
+    version = (SSLProtocolVersion)SSLDecodeInt(charPtr, 2);
+    charPtr += 2;
+    if (version != SSL_Version_2_0) {
+       sslErrorLog("SSL2ProcessServerHello: version error\n");
+        return errSSLProtocol;
+    }
+    ctx->negProtocolVersion = version;
+    sslLogNegotiateDebug("===SSL2 client: negVersion is 2_0");
+    certLen = SSLDecodeInt(charPtr, 2);
+    charPtr += 2;
+    cipherSpecsLen = SSLDecodeInt(charPtr, 2);
+    charPtr += 2;
+    connectionIDLen = SSLDecodeInt(charPtr, 2);
+    charPtr += 2;
+    
+    if (connectionIDLen < 16 || connectionIDLen > 32 || cipherSpecsLen % 3 != 0 ||
+        (msg.length != 10 + certLen + cipherSpecsLen + connectionIDLen) )
+        return errSSLProtocol;
+    if (sessionIDMatch != 0)
+    {   if (certLen != 0 || cipherSpecsLen != 0 /* || certType != 0 */ )
+            return errSSLProtocol;
+        ctx->ssl2SessionMatch = 1;
+        
+        ctx->ssl2ConnectionIDLength = connectionIDLen;
+        memcpy(ctx->serverRandom, charPtr, connectionIDLen);
+        charPtr += connectionIDLen;
+    }
+    else
+    {   if (certType != SSL2_CertTypeX509)
+            return errSSLNegotiation;
+        cipherSpecsLen /= 3;
+        
+               cert = (SSLCertificate *)sslMalloc(sizeof(SSLCertificate));
+               if(cert == NULL) {
+                       return memFullErr;
+               }
+        cert->next = 0;
+        if ((err = SSLAllocBuffer(cert->derCert, certLen, ctx)) != 0)
+        {   
+                       sslFree(cert);
+            return err;
+        }
+        memcpy(cert->derCert.data, charPtr, certLen);
+        charPtr += certLen;
+        ctx->peerCert = cert;
+       if((err = sslVerifyCertChain(ctx, *ctx->peerCert)) != 0) {
+               return err;
+       }
+        if((err = sslPubKeyFromCert(ctx, 
+               cert->derCert, 
+               &ctx->peerPubKey,
+               &ctx->peerPubKeyCsp)) != 0)
+            return err;
+        
+        selectedCipher = SSL_NO_SUCH_CIPHERSUITE;
+        for (i = 0; i < cipherSpecsLen; i++)
+        {   cipherKind = (SSL2CipherKind)SSLDecodeInt(charPtr, 3);
+            charPtr += 3;
+            if (selectedCipher == SSL_NO_SUCH_CIPHERSUITE)  /* After we find one, just keep advancing charPtr past the unused ones */
+            {   for (j = 0; j < SSL2CipherMapCount; j++)
+                    if (cipherKind == SSL2CipherMap[j].cipherKind)
+                    {   matchingCipher = SSL2CipherMap[j].cipherSuite;
+                        break;
+                    }
+                for (j = 0; j < ctx->numValidCipherSpecs; j++)   
+                    if (ctx->validCipherSpecs[j].cipherSpec == matchingCipher)
+                    {   selectedCipher = matchingCipher;
+                        break;
+                    }
+            }
+        }
+        if (selectedCipher == SSL_NO_SUCH_CIPHERSUITE)
+            return errSSLNegotiation;
+               sslLogNegotiateDebug("===SSL2 client: selectedCipher 0x%x", 
+                       (unsigned)selectedCipher);
+        
+        ctx->selectedCipher = selectedCipher;
+        if ((err = FindCipherSpec(ctx)) != 0) {
+            return err;
+        }
+        ctx->ssl2ConnectionIDLength = connectionIDLen;
+        memcpy(ctx->serverRandom, charPtr, connectionIDLen);
+        charPtr += connectionIDLen;
+    }
+    
+    assert(charPtr == msg.data + msg.length);
+    
+    return noErr;
+}
+
+OSStatus
+SSL2EncodeServerHello(SSLBuffer &msg, SSLContext *ctx)
+{   OSStatus            err;
+    SSLCertificate      *cert;
+    SSLBuffer           randomData;
+    UInt8               *charPtr;
+    unsigned            i;
+    
+    /* Create the connection ID */
+    ctx->ssl2ConnectionIDLength = SSL2_CONNECTION_ID_LENGTH;
+    randomData.data = ctx->serverRandom;
+    randomData.length = ctx->ssl2ConnectionIDLength;
+    if ((err = sslRand(ctx, &randomData)) != 0)
+        return err;
+        
+    if (ctx->ssl2SessionMatch != 0)
+    {   if ((err = SSLAllocBuffer(msg, 11 + ctx->sessionID.length, ctx)) != 0)
+            return err;
+        charPtr = msg.data;
+        *charPtr++ = SSL2_MsgServerHello;
+        *charPtr++ = ctx->ssl2SessionMatch;
+        *charPtr++ = 0;    /* cert type */
+        charPtr = SSLEncodeInt(charPtr, ctx->negProtocolVersion, 2);
+        charPtr = SSLEncodeInt(charPtr, 0, 2);    /* cert len */
+        charPtr = SSLEncodeInt(charPtr, 0, 2);    /* cipherspecs len */
+        charPtr = SSLEncodeInt(charPtr, ctx->ssl2ConnectionIDLength, 2);
+        memcpy(charPtr, ctx->serverRandom, ctx->ssl2ConnectionIDLength);
+        charPtr += ctx->ssl2ConnectionIDLength;
+    }
+    else
+    {   /* First, find the last cert in the chain; it's the one we'll send */
+    
+       /*
+        * Use encryptCert if we have it, but allow for the case of app 
+                * specifying one cert which can encrypt and sign.
+        */
+       if(ctx->encryptCert != NULL) {
+                       cert = ctx->encryptCert;
+               }
+               else if(ctx->localCert != NULL) {
+                       cert = ctx->localCert;
+               }
+               else {
+                       /* really should not happen... */
+               sslErrorLog("SSL2EncodeServerHello: No server cert!\n");
+               return badReqErr;
+       }
+        
+        while (cert->next != 0)
+            cert = cert->next;
+        
+        if ((err = SSLAllocBuffer(msg, 11 + cert->derCert.length + 3 + ctx->sessionID.length, ctx)) != 0)
+            return err;
+        charPtr = msg.data;
+        *charPtr++ = SSL2_MsgServerHello;
+        *charPtr++ = ctx->ssl2SessionMatch;
+        *charPtr++ = SSL2_CertTypeX509; /* cert type */
+
+               /* FIXME - this ifndef should not be necessary */
+               #ifndef NDEBUG
+               sslLogNegotiateDebug("===SSL2 server: sending vers info %s", 
+                       protocolVersStr((SSLProtocolVersion)ctx->negProtocolVersion));
+               #endif
+               
+        charPtr = SSLEncodeInt(charPtr, ctx->negProtocolVersion, 2);
+        charPtr = SSLEncodeInt(charPtr, cert->derCert.length, 2);
+        charPtr = SSLEncodeInt(charPtr, 3, 2);    /* cipherspecs len */
+        charPtr = SSLEncodeInt(charPtr, ctx->ssl2ConnectionIDLength, 2);
+        memcpy(charPtr, cert->derCert.data, cert->derCert.length);
+        charPtr += cert->derCert.length;
+        for (i = 0; i < SSL2CipherMapCount; i++)
+            if (ctx->selectedCipher == SSL2CipherMap[i].cipherSuite)
+                break;
+        assert(i < SSL2CipherMapCount);
+        charPtr = SSLEncodeInt(charPtr, SSL2CipherMap[i].cipherKind, 3);
+       sslLogNegotiateDebug("ssl2: server specifying cipherKind 0x%lx", 
+               (UInt32)SSL2CipherMap[i].cipherKind);
+        memcpy(charPtr, ctx->serverRandom, ctx->ssl2ConnectionIDLength);
+        charPtr += ctx->ssl2ConnectionIDLength;
+    }
+    
+    assert(charPtr == msg.data + msg.length);
+    return noErr;
+}
+
+OSStatus
+SSL2ProcessServerVerify(SSLBuffer msg, SSLContext *ctx)
+{   if (msg.length != ctx->ssl2ChallengeLength)
+        return errSSLProtocol;
+    
+    if (memcmp(msg.data, ctx->clientRandom + SSL_CLIENT_SRVR_RAND_SIZE - 
+                       ctx->ssl2ChallengeLength, ctx->ssl2ChallengeLength) != 0)
+        return errSSLProtocol;
+    
+    return noErr;
+}
+
+OSStatus
+SSL2EncodeServerVerify(SSLBuffer &msg, SSLContext *ctx)
+{   OSStatus      err;
+    
+    if ((err = SSLAllocBuffer(msg, 1 + ctx->ssl2ChallengeLength, ctx)) != 0)
+        return err;
+    
+    msg.data[0] = SSL2_MsgServerVerify;
+    memcpy(msg.data+1, ctx->clientRandom + SSL_CLIENT_SRVR_RAND_SIZE - 
+                       ctx->ssl2ChallengeLength, ctx->ssl2ChallengeLength);
+    
+    return noErr;
+}
+
+OSStatus
+SSL2ProcessServerFinished(SSLBuffer msg, SSLContext *ctx)
+{   OSStatus      err;
+    
+    if ((err = SSLAllocBuffer(ctx->sessionID, msg.length, ctx)) != 0)
+        return err;
+    memcpy(ctx->sessionID.data, msg.data, msg.length);
+    return noErr;
+}
+
+OSStatus
+SSL2EncodeServerFinished(SSLBuffer &msg, SSLContext *ctx)
+{   OSStatus      err;
+    
+    if ((err = SSLAllocBuffer(msg, 1 + ctx->sessionID.length, ctx)) != 0)
+        return err;
+    
+    msg.data[0] = SSL2_MsgServerFinished;
+    memcpy(msg.data+1, ctx->sessionID.data, ctx->sessionID.length);
+    
+    return noErr;
+}