--- /dev/null
+/*
+ * 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: sslCert.cpp
+
+ Contains: certificate request/verify messages
+
+ Written by: Doug Mitchell
+
+ Copyright: (c) 1999 by Apple Computer, Inc., all rights reserved.
+
+*/
+#include "sslContext.h"
+#include "sslHandshake.h"
+#include "sslMemory.h"
+#include "sslAlertMessage.h"
+#include "sslDebug.h"
+#include "sslUtils.h"
+#include "sslDigests.h"
+#include "appleCdsa.h"
+
+#include <string.h>
+#include <assert.h>
+
+OSStatus
+SSLEncodeCertificate(SSLRecord &certificate, SSLContext *ctx)
+{ OSStatus err;
+ UInt32 totalLength;
+ int i, j, certCount;
+ UInt8 *charPtr;
+ SSLCertificate *cert;
+
+ /*
+ * TBD: for client side, match Match DER-encoded acceptable DN list
+ * (ctx->acceptableDNList) to one of our certs. For now we just send
+ * what we have since we don't support multiple certs.
+ *
+ * Note this can be called with localCert==0 for client seide in TLS1;
+ * in that case we send an empty cert msg.
+ */
+ cert = ctx->localCert;
+ assert((ctx->negProtocolVersion == SSL_Version_3_0) ||
+ (ctx->negProtocolVersion == TLS_Version_1_0));
+ assert((cert != NULL) || (ctx->negProtocolVersion == TLS_Version_1_0));
+ totalLength = 0;
+ certCount = 0;
+ while (cert)
+ { totalLength += 3 + cert->derCert.length; /* 3 for encoded length field */
+ ++certCount;
+ cert = cert->next;
+ }
+
+ certificate.contentType = SSL_RecordTypeHandshake;
+ certificate.protocolVersion = ctx->negProtocolVersion;
+ if ((err = SSLAllocBuffer(certificate.contents, totalLength + 7, ctx)) != 0)
+ return err;
+
+ charPtr = certificate.contents.data;
+ *charPtr++ = SSL_HdskCert;
+ charPtr = SSLEncodeInt(charPtr, totalLength+3, 3); /* Handshake message length */
+ charPtr = SSLEncodeInt(charPtr, totalLength, 3); /* Vector length */
+
+ /* Root cert is first in the linked list, but has to go last,
+ * so walk list backwards */
+ for (i = 0; i < certCount; ++i)
+ { cert = ctx->localCert;
+ for (j = i+1; j < certCount; ++j)
+ cert = cert->next;
+ charPtr = SSLEncodeInt(charPtr, cert->derCert.length, 3);
+ memcpy(charPtr, cert->derCert.data, cert->derCert.length);
+ charPtr += cert->derCert.length;
+ }
+
+ assert(charPtr == certificate.contents.data + certificate.contents.length);
+
+ if ((ctx->protocolSide == SSL_ClientSide) && (ctx->localCert)) {
+ /* this tells us to send a CertificateVerify msg after the
+ * client key exchange. We skip the cert vfy if we just
+ * sent an empty cert msg (i.e., we were asked for a cert
+ * but we don't have one). */
+ ctx->certSent = 1;
+ assert(ctx->clientCertState == kSSLClientCertRequested);
+ assert(ctx->certRequested);
+ ctx->clientCertState = kSSLClientCertSent;
+ }
+ return noErr;
+}
+
+OSStatus
+SSLProcessCertificate(SSLBuffer message, SSLContext *ctx)
+{ OSStatus err;
+ UInt32 listLen, certLen;
+ UInt8 *p;
+ SSLCertificate *cert;
+
+ p = message.data;
+ listLen = SSLDecodeInt(p,3);
+ p += 3;
+ if (listLen + 3 != message.length) {
+ sslErrorLog("SSLProcessCertificate: length decode error 1\n");
+ return errSSLProtocol;
+ }
+
+ while (listLen > 0)
+ { certLen = SSLDecodeInt(p,3);
+ p += 3;
+ if (listLen < certLen + 3) {
+ sslErrorLog("SSLProcessCertificate: length decode error 2\n");
+ return errSSLProtocol;
+ }
+ cert = (SSLCertificate *)sslMalloc(sizeof(SSLCertificate));
+ if(cert == NULL) {
+ return memFullErr;
+ }
+ if ((err = SSLAllocBuffer(cert->derCert, certLen, ctx)) != 0)
+ { sslFree(cert);
+ return err;
+ }
+ memcpy(cert->derCert.data, p, certLen);
+ p += certLen;
+ cert->next = ctx->peerCert; /* Insert backwards; root cert
+ * will be first in linked list */
+ ctx->peerCert = cert;
+ listLen -= 3+certLen;
+ }
+ assert(p == message.data + message.length && listLen == 0);
+
+ if (ctx->peerCert == 0) {
+ /* this *might* be OK... */
+ if((ctx->protocolSide == SSL_ServerSide) &&
+ (ctx->clientAuth != kAlwaysAuthenticate)) {
+ /*
+ * we tried to authenticate, client doesn't have a cert, and
+ * app doesn't require it. OK.
+ */
+ return noErr;
+ }
+ else {
+ return errSSLXCertChainInvalid;
+ }
+ }
+ if((err = sslVerifyCertChain(ctx, *ctx->peerCert)) != 0)
+ return err;
+
+ /* peer's certificate is the last one in the chain */
+ cert = ctx->peerCert;
+ while (cert->next != 0)
+ cert = cert->next;
+ /* Convert its public key to CDSA format */
+ if ((err = sslPubKeyFromCert(ctx,
+ cert->derCert,
+ &ctx->peerPubKey,
+ &ctx->peerPubKeyCsp)) != 0)
+ return err;
+
+ return noErr;
+}
+
+OSStatus
+SSLEncodeCertificateRequest(SSLRecord &request, SSLContext *ctx)
+{
+ OSStatus err;
+ UInt32 dnListLen, msgLen;
+ UInt8 *charPtr;
+ DNListElem *dn;
+
+ assert(ctx->protocolSide == SSL_ServerSide);
+ dnListLen = 0;
+ dn = ctx->acceptableDNList;
+ while (dn)
+ { dnListLen += 2 + dn->derDN.length;
+ dn = dn->next;
+ }
+ msgLen = 1 + 1 + 2 + dnListLen;
+
+ request.contentType = SSL_RecordTypeHandshake;
+ assert((ctx->negProtocolVersion == SSL_Version_3_0) ||
+ (ctx->negProtocolVersion == TLS_Version_1_0));
+ request.protocolVersion = ctx->negProtocolVersion;
+ if ((err = SSLAllocBuffer(request.contents, msgLen + 4, ctx)) != 0)
+ return err;
+
+ charPtr = request.contents.data;
+ *charPtr++ = SSL_HdskCertRequest;
+ charPtr = SSLEncodeInt(charPtr, msgLen, 3);
+
+ *charPtr++ = 1; /* one cert type */
+ *charPtr++ = 1; /* RSA-sign type */
+ charPtr = SSLEncodeInt(charPtr, dnListLen, 2);
+ dn = ctx->acceptableDNList;
+ while (dn)
+ { charPtr = SSLEncodeInt(charPtr, dn->derDN.length, 2);
+ memcpy(charPtr, dn->derDN.data, dn->derDN.length);
+ charPtr += dn->derDN.length;
+ dn = dn->next;
+ }
+
+ assert(charPtr == request.contents.data + request.contents.length);
+ return noErr;
+}
+
+OSStatus
+SSLProcessCertificateRequest(SSLBuffer message, SSLContext *ctx)
+{
+ unsigned i;
+ unsigned typeCount;
+ UInt8 *charPtr;
+
+ /*
+ * Cert request only happens in during client authentication, which
+ * we don't do. We will however take this handshake msg and do
+ * nothing with the enclosed DNList. We'll send a client cert
+ * if we have one but we don't do any DNList compare.
+ */
+ if (message.length < 3) {
+ sslErrorLog("SSLProcessCertificateRequest: length decode error 1\n");
+ return errSSLProtocol;
+ }
+ charPtr = message.data;
+ typeCount = *charPtr++;
+ if (typeCount < 1 || message.length < 3 + typeCount) {
+ sslErrorLog("SSLProcessCertificateRequest: length decode error 2\n");
+ return errSSLProtocol;
+ }
+ for (i = 0; i < typeCount; i++)
+ { if (*charPtr++ == 1)
+ ctx->x509Requested = 1;
+ }
+
+ #if 0
+ /* FIXME - currently untested */
+ unsigned dnListLen;
+ unsigned dnLen;
+ SSLBuffer dnBuf;
+ DNListElem *dn;
+ OSStatus err;
+
+ dnListLen = SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+ if (message.length != 3 + typeCount + dnListLen) {
+ sslErrorLog("SSLProcessCertificateRequest: length decode error 3\n");
+ return errSSLProtocol;
+ }
+ while (dnListLen > 0)
+ { if (dnListLen < 2) {
+ sslErrorLog("SSLProcessCertificateRequest: dnListLen error 1\n");
+ return errSSLProtocol;
+ }
+ dnLen = SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+ if (dnListLen < 2 + dnLen) {
+ sslErrorLog("SSLProcessCertificateRequest: dnListLen error 2\n");
+ return errSSLProtocol;
+ }
+ if ((err = SSLAllocBuffer(dnBuf, sizeof(DNListElem), ctx)) != 0)
+ return err;
+ dn = (DNListElem*)dnBuf.data;
+ if ((err = SSLAllocBuffer(dn->derDN, dnLen, ctx)) != 0)
+ { SSLFreeBuffer(dnBuf, ctx);
+ return err;
+ }
+ memcpy(dn->derDN.data, charPtr, dnLen);
+ charPtr += dnLen;
+ dn->next = ctx->acceptableDNList;
+ ctx->acceptableDNList = dn;
+ dnListLen -= 2 + dnLen;
+ }
+
+ assert(charPtr == message.data + message.length);
+ #endif /* untested client-side authentication */
+
+ return noErr;
+}
+
+OSStatus
+SSLEncodeCertificateVerify(SSLRecord &certVerify, SSLContext *ctx)
+{ OSStatus err;
+ UInt8 hashData[36];
+ SSLBuffer hashDataBuf, shaMsgState, md5MsgState;
+ UInt32 len;
+ UInt32 outputLen;
+
+ certVerify.contents.data = 0;
+ hashDataBuf.data = hashData;
+ hashDataBuf.length = 36;
+
+ if ((err = CloneHashState(SSLHashSHA1, ctx->shaState, shaMsgState, ctx)) != 0)
+ goto fail;
+ if ((err = CloneHashState(SSLHashMD5, ctx->md5State, md5MsgState, ctx)) != 0)
+ goto fail;
+ assert(ctx->sslTslCalls != NULL);
+ if ((err = ctx->sslTslCalls->computeCertVfyMac(ctx, hashDataBuf,
+ shaMsgState, md5MsgState)) != 0)
+ goto fail;
+
+ assert(ctx->signingPrivKey != NULL);
+ len = sslKeyLengthInBytes(ctx->signingPrivKey);
+
+ certVerify.contentType = SSL_RecordTypeHandshake;
+ assert((ctx->negProtocolVersion == SSL_Version_3_0) ||
+ (ctx->negProtocolVersion == TLS_Version_1_0));
+ certVerify.protocolVersion = ctx->negProtocolVersion;
+ if ((err = SSLAllocBuffer(certVerify.contents, len + 6, ctx)) != 0)
+ goto fail;
+
+ certVerify.contents.data[0] = SSL_HdskCertVerify;
+ SSLEncodeInt(certVerify.contents.data+1, len+2, 3);
+ SSLEncodeInt(certVerify.contents.data+4, len, 2);
+
+ err = sslRsaRawSign(ctx,
+ ctx->signingPrivKey,
+ ctx->signingKeyCsp,
+ hashData, // data to sign
+ 36, // MD5 size + SHA1 size
+ certVerify.contents.data+6, // signature destination
+ len, // we mallocd len+6
+ &outputLen);
+ if(err) {
+ goto fail;
+ }
+
+ assert(outputLen == len);
+
+ err = noErr;
+
+fail:
+ SSLFreeBuffer(shaMsgState, ctx);
+ SSLFreeBuffer(md5MsgState, ctx);
+
+ return err;
+}
+
+OSStatus
+SSLProcessCertificateVerify(SSLBuffer message, SSLContext *ctx)
+{ OSStatus err;
+ UInt8 hashData[36];
+ UInt16 signatureLen;
+ SSLBuffer hashDataBuf, shaMsgState, md5MsgState;
+ unsigned int publicModulusLen;
+
+ shaMsgState.data = 0;
+ md5MsgState.data = 0;
+
+ if (message.length < 2) {
+ sslErrorLog("SSLProcessCertificateVerify: msg len error\n");
+ return errSSLProtocol;
+ }
+
+ signatureLen = (UInt16)SSLDecodeInt(message.data, 2);
+ if (message.length != (unsigned)(2 + signatureLen)) {
+ sslErrorLog("SSLProcessCertificateVerify: sig len error 1\n");
+ return errSSLProtocol;
+ }
+
+ assert(ctx->peerPubKey != NULL);
+ publicModulusLen = sslKeyLengthInBytes(ctx->peerPubKey);
+
+ if (signatureLen != publicModulusLen) {
+ sslErrorLog("SSLProcessCertificateVerify: sig len error 2\n");
+ return errSSLProtocol;
+ }
+ hashDataBuf.data = hashData;
+ hashDataBuf.length = 36;
+
+ if ((err = CloneHashState(SSLHashSHA1, ctx->shaState, shaMsgState, ctx)) != 0)
+ goto fail;
+ if ((err = CloneHashState(SSLHashMD5, ctx->md5State, md5MsgState, ctx)) != 0)
+ goto fail;
+ assert(ctx->sslTslCalls != NULL);
+ if ((err = ctx->sslTslCalls->computeCertVfyMac(ctx, hashDataBuf,
+ shaMsgState, md5MsgState)) != 0)
+ goto fail;
+
+ /*
+ * The CSP does the decrypt & compare for us in one shot
+ */
+ err = sslRsaRawVerify(ctx,
+ ctx->peerPubKey,
+ ctx->peerPubKeyCsp, // FIXME - maybe we just use cspHand?
+ hashData, // data to verify
+ 36,
+ message.data + 2, // signature
+ signatureLen);
+ if(err) {
+ goto fail;
+ }
+ err = noErr;
+
+fail:
+ SSLFreeBuffer(shaMsgState, ctx);
+ SSLFreeBuffer(md5MsgState, ctx);
+
+ return err;
+}