Back to index

lightning-sunbird  0.9+nobinonly
derive.c
Go to the documentation of this file.
00001 /*
00002  * Key Derivation that doesn't use PKCS11
00003  *
00004  * ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is the Netscape security libraries.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 1994-2005
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 /* $Id: derive.c,v 1.3.2.1 2006/08/24 22:31:54 nelson%bolyard.com Exp $ */
00040 
00041 #include "ssl.h"     /* prereq to sslimpl.h */
00042 #include "certt.h"   /* prereq to sslimpl.h */
00043 #include "keythi.h"  /* prereq to sslimpl.h */
00044 #include "sslimpl.h"
00045 #include "blapi.h"
00046 
00047 /* make this a macro! */
00048 #ifdef NOT_A_MACRO
00049 static void
00050 buildSSLKey(unsigned char * keyBlock, unsigned int keyLen, SECItem * result)
00051 {
00052     result->type = siBuffer;
00053     result->data = keyBlock;
00054     result->len  = keyLen;
00055     PRINT_BUF(100, (NULL, "key value", keyBlock, keyLen));
00056 }
00057 #else
00058 #define buildSSLKey(keyBlock, keyLen, result) \
00059 { \
00060     (result)->type = siBuffer; \
00061     (result)->data = keyBlock; \
00062     (result)->len  = keyLen; \
00063     PRINT_BUF(100, (NULL, "key value", keyBlock, keyLen)); \
00064 }
00065 #endif
00066 
00067 /*
00068  * SSL Key generation given pre master secret
00069  */
00070 #ifndef NUM_MIXERS
00071 #define NUM_MIXERS 9
00072 #endif
00073 static const char * const mixers[NUM_MIXERS] = { 
00074     "A", 
00075     "BB", 
00076     "CCC", 
00077     "DDDD", 
00078     "EEEEE", 
00079     "FFFFFF", 
00080     "GGGGGGG",
00081     "HHHHHHHH",
00082     "IIIIIIIII" 
00083 };
00084 
00085 
00086 SECStatus
00087 ssl3_KeyAndMacDeriveBypass(
00088     ssl3CipherSpec *      pwSpec,
00089     const unsigned char * cr,
00090     const unsigned char * sr,
00091     PRBool                isTLS,
00092     PRBool                isExport)
00093 {
00094     const ssl3BulkCipherDef *cipher_def = pwSpec->cipher_def;
00095     unsigned char * key_block    = pwSpec->key_block;
00096     unsigned char * key_block2   = NULL;
00097     unsigned int    block_bytes  = 0;
00098     unsigned int    block_needed = 0;
00099     unsigned int    i;
00100     unsigned int    keySize;            /* actual    size of cipher keys */
00101     unsigned int    effKeySize;           /* effective size of cipher keys */
00102     unsigned int    macSize;              /* size of MAC secret */
00103     unsigned int    IVSize;        /* size of IV */
00104     SECStatus       rv    = SECFailure;
00105     SECStatus       status = SECSuccess;
00106     PRBool          isFIPS = PR_FALSE;
00107 
00108     SECItem         srcr;
00109     SECItem         crsr;
00110 
00111     unsigned char     srcrdata[SSL3_RANDOM_LENGTH * 2];
00112     unsigned char     crsrdata[SSL3_RANDOM_LENGTH * 2];
00113     PRUint64          md5buf[22];
00114     PRUint64          shabuf[40];
00115 
00116 #define md5Ctx ((MD5Context *)md5buf)
00117 #define shaCtx ((SHA1Context *)shabuf)
00118 
00119     static const SECItem zed  = { siBuffer, NULL, 0 };
00120 
00121     if (pwSpec->msItem.data == NULL ||
00122         pwSpec->msItem.len  != SSL3_MASTER_SECRET_LENGTH) {
00123        PORT_SetError(SEC_ERROR_INVALID_ARGS);
00124        return rv;
00125     }
00126 
00127     PRINT_BUF(100, (NULL, "Master Secret", pwSpec->msItem.data, 
00128                                            pwSpec->msItem.len));
00129 
00130     /* figure out how much is needed */
00131     macSize    = pwSpec->mac_size;
00132     keySize    = cipher_def->key_size;
00133     effKeySize = cipher_def->secret_key_size;
00134     IVSize     = cipher_def->iv_size;
00135     if (keySize == 0) {
00136        effKeySize = IVSize = 0; /* only MACing */
00137     }
00138     block_needed = 2 * (macSize + effKeySize + ((!isExport) * IVSize));
00139 
00140     /*
00141      * clear out our returned keys so we can recover on failure
00142      */
00143     pwSpec->client.write_key_item     = zed;
00144     pwSpec->client.write_mac_key_item = zed;
00145     pwSpec->server.write_key_item     = zed;
00146     pwSpec->server.write_mac_key_item = zed;
00147 
00148     /* initialize the server random, client random block */
00149     srcr.type   = siBuffer;
00150     srcr.data   = srcrdata;
00151     srcr.len    = sizeof srcrdata;
00152     PORT_Memcpy(srcrdata, sr, SSL3_RANDOM_LENGTH);
00153     PORT_Memcpy(srcrdata + SSL3_RANDOM_LENGTH, cr, SSL3_RANDOM_LENGTH);
00154 
00155     /* initialize the client random, server random block */
00156     crsr.type   = siBuffer;
00157     crsr.data   = crsrdata;
00158     crsr.len    = sizeof crsrdata;
00159     PORT_Memcpy(crsrdata, cr, SSL3_RANDOM_LENGTH);
00160     PORT_Memcpy(crsrdata + SSL3_RANDOM_LENGTH, sr, SSL3_RANDOM_LENGTH);
00161     PRINT_BUF(100, (NULL, "Key & MAC CRSR", crsr.data, crsr.len));
00162 
00163     /*
00164      * generate the key material:
00165      */
00166     if (isTLS) {
00167        SECItem       keyblk;
00168 
00169        keyblk.type = siBuffer;
00170        keyblk.data = key_block;
00171        keyblk.len  = block_needed;
00172 
00173        status = TLS_PRF(&pwSpec->msItem, "key expansion", &srcr, &keyblk,
00174                        isFIPS);
00175        if (status != SECSuccess) {
00176            goto key_and_mac_derive_fail;
00177        }
00178        block_bytes = keyblk.len;
00179     } else {
00180        /* key_block = 
00181         *     MD5(master_secret + SHA('A' + master_secret + 
00182         *                      ServerHello.random + ClientHello.random)) +
00183         *     MD5(master_secret + SHA('BB' + master_secret + 
00184         *                      ServerHello.random + ClientHello.random)) +
00185         *     MD5(master_secret + SHA('CCC' + master_secret + 
00186         *                      ServerHello.random + ClientHello.random)) +
00187         *     [...];
00188         */
00189        int made = 0;
00190        for (i = 0; made < block_needed && i < NUM_MIXERS; ++i) {
00191            unsigned int    outLen;
00192            unsigned char   sha_out[SHA1_LENGTH];
00193 
00194            SHA1_Begin(shaCtx);
00195            SHA1_Update(shaCtx, (unsigned char*)(mixers[i]), i+1);
00196            SHA1_Update(shaCtx, pwSpec->msItem.data, pwSpec->msItem.len);
00197            SHA1_Update(shaCtx, srcr.data, srcr.len);
00198            SHA1_End(shaCtx, sha_out, &outLen, SHA1_LENGTH);
00199            PORT_Assert(outLen == SHA1_LENGTH);
00200 
00201            MD5_Begin(md5Ctx);
00202            MD5_Update(md5Ctx, pwSpec->msItem.data, pwSpec->msItem.len);
00203            MD5_Update(md5Ctx, sha_out, outLen);
00204            MD5_End(md5Ctx, key_block + made, &outLen, MD5_LENGTH);
00205            PORT_Assert(outLen == MD5_LENGTH);
00206            made += MD5_LENGTH;
00207        }
00208        block_bytes = made;
00209     }
00210     PORT_Assert(block_bytes >= block_needed);
00211     PORT_Assert(block_bytes <= sizeof pwSpec->key_block);
00212     PRINT_BUF(100, (NULL, "key block", key_block, block_bytes));
00213 
00214     /*
00215      * Put the key material where it goes.
00216      */
00217     key_block2 = key_block + block_bytes;
00218     i = 0;                  /* now shows how much consumed */
00219 
00220     /* 
00221      * The key_block is partitioned as follows:
00222      * client_write_MAC_secret[CipherSpec.hash_size]
00223      */
00224     buildSSLKey(&key_block[i],macSize, &pwSpec->client.write_mac_key_item);
00225     i += macSize;
00226 
00227     /* 
00228      * server_write_MAC_secret[CipherSpec.hash_size]
00229      */
00230     buildSSLKey(&key_block[i],macSize, &pwSpec->server.write_mac_key_item);
00231     i += macSize;
00232 
00233     if (!keySize) {
00234        /* only MACing */
00235        buildSSLKey(NULL, 0, &pwSpec->client.write_key_item);
00236        buildSSLKey(NULL, 0, &pwSpec->server.write_key_item);
00237        buildSSLKey(NULL, 0, &pwSpec->client.write_iv_item);
00238        buildSSLKey(NULL, 0, &pwSpec->server.write_iv_item);
00239     } else if (!isExport) {
00240        /* 
00241        ** Generate Domestic write keys and IVs.
00242        ** client_write_key[CipherSpec.key_material]
00243        */
00244        buildSSLKey(&key_block[i], keySize, &pwSpec->client.write_key_item);
00245        i += keySize;
00246 
00247        /* 
00248        ** server_write_key[CipherSpec.key_material]
00249        */
00250        buildSSLKey(&key_block[i], keySize, &pwSpec->server.write_key_item);
00251        i += keySize;
00252 
00253        if (IVSize > 0) {
00254            /* 
00255            ** client_write_IV[CipherSpec.IV_size]
00256            */
00257            buildSSLKey(&key_block[i], IVSize, &pwSpec->client.write_iv_item);
00258            i += IVSize;
00259 
00260            /* 
00261            ** server_write_IV[CipherSpec.IV_size]
00262            */
00263            buildSSLKey(&key_block[i], IVSize, &pwSpec->server.write_iv_item);
00264            i += IVSize;
00265        }
00266        PORT_Assert(i <= block_bytes);
00267 
00268     } else if (!isTLS) { 
00269        /*
00270        ** Generate SSL3 Export write keys and IVs.
00271        */
00272        unsigned int    outLen;
00273 
00274        /*
00275        ** client_write_key[CipherSpec.key_material]
00276        ** final_client_write_key = MD5(client_write_key +
00277        **                   ClientHello.random + ServerHello.random);
00278        */
00279        MD5_Begin(md5Ctx);
00280        MD5_Update(md5Ctx, &key_block[i], effKeySize);
00281        MD5_Update(md5Ctx, crsr.data, crsr.len);
00282        MD5_End(md5Ctx, key_block2, &outLen, MD5_LENGTH);
00283        i += effKeySize;
00284        buildSSLKey(key_block2, keySize, &pwSpec->client.write_key_item);
00285        key_block2 += keySize;
00286 
00287        /*
00288        ** server_write_key[CipherSpec.key_material]
00289        ** final_server_write_key = MD5(server_write_key +
00290        **                    ServerHello.random + ClientHello.random);
00291        */
00292        MD5_Begin(md5Ctx);
00293        MD5_Update(md5Ctx, &key_block[i], effKeySize);
00294        MD5_Update(md5Ctx, srcr.data, srcr.len);
00295        MD5_End(md5Ctx, key_block2, &outLen, MD5_LENGTH);
00296        i += effKeySize;
00297        buildSSLKey(key_block2, keySize, &pwSpec->server.write_key_item);
00298        key_block2 += keySize;
00299        PORT_Assert(i <= block_bytes);
00300 
00301        if (IVSize) {
00302            /*
00303            ** client_write_IV = 
00304            ** MD5(ClientHello.random + ServerHello.random);
00305            */
00306            MD5_Begin(md5Ctx);
00307            MD5_Update(md5Ctx, crsr.data, crsr.len);
00308            MD5_End(md5Ctx, key_block2, &outLen, MD5_LENGTH);
00309            buildSSLKey(key_block2, IVSize, &pwSpec->client.write_iv_item);
00310            key_block2 += IVSize;
00311 
00312            /*
00313            ** server_write_IV = 
00314            ** MD5(ServerHello.random + ClientHello.random);
00315            */
00316            MD5_Begin(md5Ctx);
00317            MD5_Update(md5Ctx, srcr.data, srcr.len);
00318            MD5_End(md5Ctx, key_block2, &outLen, MD5_LENGTH);
00319            buildSSLKey(key_block2, IVSize, &pwSpec->server.write_iv_item);
00320            key_block2 += IVSize;
00321        }
00322 
00323        PORT_Assert(key_block2 - key_block <= sizeof pwSpec->key_block);
00324     } else {
00325        /*
00326        ** Generate TLS Export write keys and IVs.
00327        */
00328        SECItem       secret ;
00329        SECItem       keyblk ;
00330 
00331        secret.type = siBuffer;
00332        keyblk.type = siBuffer;
00333        /*
00334        ** client_write_key[CipherSpec.key_material]
00335        ** final_client_write_key = PRF(client_write_key, 
00336        **                              "client write key",
00337        **                              client_random + server_random);
00338        */
00339        secret.data = &key_block[i];
00340        secret.len  = effKeySize;
00341        i          += effKeySize;
00342        keyblk.data = key_block2;
00343        keyblk.len  = keySize;
00344        status = TLS_PRF(&secret, "client write key", &crsr, &keyblk, isFIPS);
00345        if (status != SECSuccess) {
00346            goto key_and_mac_derive_fail;
00347        }
00348        buildSSLKey(key_block2, keySize, &pwSpec->client.write_key_item);
00349        key_block2 += keySize;
00350 
00351        /*
00352        ** server_write_key[CipherSpec.key_material]
00353        ** final_server_write_key = PRF(server_write_key,
00354        **                              "server write key",
00355        **                              client_random + server_random);
00356        */
00357        secret.data = &key_block[i];
00358        secret.len  = effKeySize;
00359        i          += effKeySize;
00360        keyblk.data = key_block2;
00361        keyblk.len  = keySize;
00362        status = TLS_PRF(&secret, "server write key", &crsr, &keyblk, isFIPS);
00363        if (status != SECSuccess) {
00364            goto key_and_mac_derive_fail;
00365        }
00366        buildSSLKey(key_block2, keySize, &pwSpec->server.write_key_item);
00367        key_block2 += keySize;
00368 
00369        /*
00370        ** iv_block = PRF("", "IV block", client_random + server_random);
00371        ** client_write_IV[SecurityParameters.IV_size]
00372        ** server_write_IV[SecurityParameters.IV_size]
00373        */
00374        if (IVSize) {
00375            secret.data = NULL;
00376            secret.len  = 0;
00377            keyblk.data = key_block2;
00378            keyblk.len  = 2 * IVSize;
00379            status = TLS_PRF(&secret, "IV block", &crsr, &keyblk, isFIPS);
00380            if (status != SECSuccess) {
00381               goto key_and_mac_derive_fail;
00382            }
00383            buildSSLKey(key_block2,          IVSize, &pwSpec->client.write_iv_item);
00384            buildSSLKey(key_block2 + IVSize, IVSize, &pwSpec->server.write_iv_item);
00385            key_block2 += 2 * IVSize;
00386        }
00387        PORT_Assert(key_block2 - key_block <= sizeof pwSpec->key_block);
00388     }
00389     rv = SECSuccess;
00390 
00391 key_and_mac_derive_fail:
00392 
00393     MD5_DestroyContext(md5Ctx, PR_FALSE);
00394     SHA1_DestroyContext(shaCtx, PR_FALSE);
00395 
00396     if (rv != SECSuccess) {
00397        PORT_SetError(SSL_ERROR_SESSION_KEY_GEN_FAILURE);
00398     }
00399 
00400     return rv;
00401 }
00402 
00403 
00404 /* derive the Master Secret from the PMS */
00405 /* Presently, this is only done wtih RSA PMS, and only on the server side,
00406  * so isRSA is always true. 
00407  */
00408 SECStatus
00409 ssl3_MasterKeyDeriveBypass( 
00410     ssl3CipherSpec *      pwSpec,
00411     const unsigned char * cr,
00412     const unsigned char * sr,
00413     const SECItem *       pms,
00414     PRBool                isTLS,
00415     PRBool                isRSA)
00416 {
00417     unsigned char * key_block    = pwSpec->key_block;
00418     SECStatus       rv    = SECSuccess;
00419     PRBool          isFIPS = PR_FALSE;
00420 
00421     SECItem         crsr;
00422 
00423     unsigned char     crsrdata[SSL3_RANDOM_LENGTH * 2];
00424     PRUint64          md5buf[22];
00425     PRUint64          shabuf[40];
00426 
00427 #define md5Ctx ((MD5Context *)md5buf)
00428 #define shaCtx ((SHA1Context *)shabuf)
00429 
00430     /* first do the consistancy checks */
00431     if (isRSA) { 
00432        PORT_Assert(pms->len == SSL3_RSA_PMS_LENGTH);
00433        if (pms->len != SSL3_RSA_PMS_LENGTH) {
00434            PORT_SetError(SEC_ERROR_INVALID_ARGS);
00435            return SECFailure;
00436        }
00437        /* caller must test PMS version for rollback */
00438     }
00439 
00440     /* initialize the client random, server random block */
00441     crsr.type   = siBuffer;
00442     crsr.data   = crsrdata;
00443     crsr.len    = sizeof crsrdata;
00444     PORT_Memcpy(crsrdata, cr, SSL3_RANDOM_LENGTH);
00445     PORT_Memcpy(crsrdata + SSL3_RANDOM_LENGTH, sr, SSL3_RANDOM_LENGTH);
00446     PRINT_BUF(100, (NULL, "Master Secret CRSR", crsr.data, crsr.len));
00447 
00448     /* finally do the key gen */
00449     if (isTLS) {
00450        SECItem master = { siBuffer, NULL, 0 };
00451 
00452        master.data = key_block;
00453        master.len = SSL3_MASTER_SECRET_LENGTH;
00454 
00455        rv = TLS_PRF(pms, "master secret", &crsr, &master, isFIPS);
00456        if (rv != SECSuccess) {
00457            PORT_SetError(SSL_ERROR_SESSION_KEY_GEN_FAILURE);
00458        }
00459     } else {
00460        int i;
00461        int made = 0;
00462        for (i = 0; i < 3; i++) {
00463            unsigned int    outLen;
00464            unsigned char   sha_out[SHA1_LENGTH];
00465 
00466            SHA1_Begin(shaCtx);
00467            SHA1_Update(shaCtx, (unsigned char*) mixers[i], i+1);
00468            SHA1_Update(shaCtx, pms->data, pms->len);
00469            SHA1_Update(shaCtx, crsr.data, crsr.len);
00470            SHA1_End(shaCtx, sha_out, &outLen, SHA1_LENGTH);
00471            PORT_Assert(outLen == SHA1_LENGTH);
00472 
00473            MD5_Begin(md5Ctx);
00474            MD5_Update(md5Ctx, pms->data, pms->len);
00475            MD5_Update(md5Ctx, sha_out, outLen);
00476            MD5_End(md5Ctx, key_block + made, &outLen, MD5_LENGTH);
00477            PORT_Assert(outLen == MD5_LENGTH);
00478            made += outLen;
00479        }
00480     }
00481 
00482     /* store the results */
00483     PORT_Memcpy(pwSpec->raw_master_secret, key_block, 
00484               SSL3_MASTER_SECRET_LENGTH);
00485     pwSpec->msItem.data = pwSpec->raw_master_secret;
00486     pwSpec->msItem.len  = SSL3_MASTER_SECRET_LENGTH;
00487     PRINT_BUF(100, (NULL, "Master Secret", pwSpec->msItem.data, 
00488                                            pwSpec->msItem.len));
00489 
00490     return rv;
00491 }
00492 
00493