]> git.saurik.com Git - apple/security.git/blobdiff - SecureTransport/sslCert.cpp
Security-54.1.3.tar.gz
[apple/security.git] / SecureTransport / sslCert.cpp
diff --git a/SecureTransport/sslCert.cpp b/SecureTransport/sslCert.cpp
new file mode 100644 (file)
index 0000000..7bf7b1b
--- /dev/null
@@ -0,0 +1,410 @@
+/*
+ * 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;
+}