Back to index

lightning-sunbird  0.9+nobinonly
lowpbe.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 #include "plarena.h"
00038 
00039 #include "seccomon.h"
00040 #include "secitem.h"
00041 #include "secport.h"
00042 #include "hasht.h"
00043 #include "pkcs11t.h"
00044 #include "blapi.h"
00045 #include "hasht.h"
00046 #include "secasn1.h"
00047 #include "secder.h"
00048 #include "lowpbe.h"
00049 #include "secoid.h"
00050 #include "alghmac.h"
00051 #include "softoken.h"
00052 #include "secerr.h"
00053 
00054 /* template for PKCS 5 PBE Parameter.  This template has been expanded
00055  * based upon the additions in PKCS 12.  This should eventually be moved
00056  * if RSA updates PKCS 5.
00057  */
00058 static const SEC_ASN1Template NSSPKCS5PBEParameterTemplate[] =
00059 {
00060     { SEC_ASN1_SEQUENCE, 
00061        0, NULL, sizeof(NSSPKCS5PBEParameter) },
00062     { SEC_ASN1_OCTET_STRING, 
00063        offsetof(NSSPKCS5PBEParameter, salt) },
00064     { SEC_ASN1_INTEGER,
00065        offsetof(NSSPKCS5PBEParameter, iteration) },
00066     { 0 }
00067 };
00068 
00069 static const SEC_ASN1Template NSSPKCS5PKCS12V2PBEParameterTemplate[] =
00070 {   
00071     { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSPKCS5PBEParameter) },
00072     { SEC_ASN1_OCTET_STRING, offsetof(NSSPKCS5PBEParameter, salt) },
00073     { SEC_ASN1_INTEGER, offsetof(NSSPKCS5PBEParameter, iteration) },
00074     { 0 }
00075 };
00076 
00077 SECStatus
00078 nsspkcs5_HashBuf(const SECHashObject *hashObj, unsigned char *dest,
00079                                     unsigned char *src, int len)
00080 {
00081     void *ctx;
00082     unsigned int retLen;
00083 
00084     ctx = hashObj->create();
00085     if(ctx == NULL) {
00086        return SECFailure;
00087     }
00088     hashObj->begin(ctx);
00089     hashObj->update(ctx, src, len);
00090     hashObj->end(ctx, dest, &retLen, hashObj->length);
00091     hashObj->destroy(ctx, PR_TRUE);
00092     return SECSuccess;
00093 }
00094 
00095 /* generate bits using any hash
00096  */
00097 static SECItem *
00098 nsspkcs5_PBKDF1(const SECHashObject *hashObj, SECItem *salt, SECItem *pwd, 
00099                                           int iter, PRBool faulty3DES) 
00100 {
00101     SECItem *hash = NULL, *pre_hash = NULL;
00102     SECStatus rv = SECFailure;
00103 
00104     if((salt == NULL) || (pwd == NULL) || (iter < 0)) {
00105        return NULL;
00106     }
00107        
00108     hash = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
00109     pre_hash = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
00110 
00111     if((hash != NULL) && (pre_hash != NULL)) {
00112        int i, ph_len;
00113 
00114        ph_len = hashObj->length;
00115        if((salt->len + pwd->len) > hashObj->length) {
00116            ph_len = salt->len + pwd->len;
00117        }
00118 
00119        rv = SECFailure;
00120 
00121        /* allocate buffers */
00122        hash->len = hashObj->length;
00123        hash->data = (unsigned char *)PORT_ZAlloc(hash->len);
00124        pre_hash->data = (unsigned char *)PORT_ZAlloc(ph_len);
00125 
00126        /* in pbeSHA1TripleDESCBC there was an allocation error that made
00127         * it into the caller.  We do not want to propagate those errors
00128         * further, so we are doing it correctly, but reading the old method.
00129         */
00130        if (faulty3DES) {
00131            pre_hash->len = ph_len;
00132        } else {
00133            pre_hash->len = salt->len + pwd->len;
00134        }
00135 
00136        /* preform hash */
00137        if ((hash->data != NULL) && (pre_hash->data != NULL)) {
00138            rv = SECSuccess;
00139            /* check for 0 length password */
00140            if(pwd->len > 0) {
00141               PORT_Memcpy(pre_hash->data, pwd->data, pwd->len);
00142            }
00143            if(salt->len > 0) {
00144               PORT_Memcpy((pre_hash->data+pwd->len), salt->data, salt->len);
00145            }
00146            for(i = 0; ((i < iter) && (rv == SECSuccess)); i++) {
00147               rv = nsspkcs5_HashBuf(hashObj, hash->data, 
00148                                    pre_hash->data, pre_hash->len);
00149               if(rv != SECFailure) {
00150                   pre_hash->len = hashObj->length;
00151                   PORT_Memcpy(pre_hash->data, hash->data, hashObj->length);
00152               }
00153            }
00154        }
00155     }
00156 
00157     if(pre_hash != NULL) {
00158        SECITEM_FreeItem(pre_hash, PR_TRUE);
00159     }
00160 
00161     if((rv != SECSuccess) && (hash != NULL)) {
00162        SECITEM_FreeItem(hash, PR_TRUE);
00163        hash = NULL;
00164     }
00165 
00166     return hash;
00167 }
00168 
00169 /* this bit generation routine is described in PKCS 12 and the proposed
00170  * extensions to PKCS 5.  an initial hash is generated following the
00171  * instructions laid out in PKCS 5.  If the number of bits generated is
00172  * insufficient, then the method discussed in the proposed extensions to
00173  * PKCS 5 in PKCS 12 are used.  This extension makes use of the HMAC
00174  * function.  And the P_Hash function from the TLS standard.
00175  */
00176 static SECItem *
00177 nsspkcs5_PFXPBE(const SECHashObject *hashObj, NSSPKCS5PBEParameter *pbe_param,
00178                             SECItem *init_hash, unsigned int bytes_needed)
00179 {
00180     SECItem *ret_bits = NULL;
00181     int hash_size = 0;
00182     unsigned int i;
00183     unsigned int hash_iter;
00184     unsigned int dig_len;
00185     SECStatus rv = SECFailure;
00186     unsigned char *state = NULL;
00187     unsigned int state_len;
00188     HMACContext *cx = NULL;
00189 
00190     hash_size = hashObj->length;
00191     hash_iter = (bytes_needed + (unsigned int)hash_size - 1) / hash_size;
00192 
00193     /* allocate return buffer */
00194     ret_bits = (SECItem  *)PORT_ZAlloc(sizeof(SECItem));
00195     if(ret_bits == NULL)
00196        return NULL;
00197     ret_bits->data = (unsigned char *)PORT_ZAlloc((hash_iter * hash_size) + 1);
00198     ret_bits->len = (hash_iter * hash_size);
00199     if(ret_bits->data == NULL) {
00200        PORT_Free(ret_bits);
00201        return NULL;
00202     }
00203 
00204     /* allocate intermediate hash buffer.  8 is for the 8 bytes of
00205      * data which are added based on iteration number 
00206      */
00207 
00208     if ((unsigned int)hash_size > pbe_param->salt.len) {
00209        state_len = hash_size;
00210     } else {
00211        state_len = pbe_param->salt.len;
00212     }
00213     state = (unsigned char *)PORT_ZAlloc(state_len);
00214     if(state == NULL) {
00215        rv = SECFailure;
00216        goto loser;
00217     }
00218     if(pbe_param->salt.len > 0) {
00219        PORT_Memcpy(state, pbe_param->salt.data, pbe_param->salt.len);
00220     }
00221 
00222     cx = HMAC_Create(hashObj, init_hash->data, init_hash->len, PR_TRUE);
00223     if (cx == NULL) {
00224        rv = SECFailure;
00225        goto loser;
00226     }
00227 
00228     for(i = 0; i < hash_iter; i++) { 
00229 
00230        /* generate output bits */
00231        HMAC_Begin(cx);
00232        HMAC_Update(cx, state, state_len);
00233        HMAC_Update(cx, pbe_param->salt.data, pbe_param->salt.len);
00234        rv = HMAC_Finish(cx, ret_bits->data + (i * hash_size),
00235                       &dig_len, hash_size);
00236        if (rv != SECSuccess)
00237            goto loser;
00238        PORT_Assert((unsigned int)hash_size == dig_len);
00239 
00240        /* generate new state */
00241        HMAC_Begin(cx);
00242        HMAC_Update(cx, state, state_len);
00243        rv = HMAC_Finish(cx, state, &state_len, state_len);
00244        if (rv != SECSuccess)
00245            goto loser;
00246        PORT_Assert(state_len == dig_len);
00247     }
00248 
00249 loser:
00250     if (state != NULL)
00251        PORT_ZFree(state, state_len);
00252     HMAC_Destroy(cx, PR_TRUE);
00253 
00254     if(rv != SECSuccess) {
00255        SECITEM_ZfreeItem(ret_bits, PR_TRUE);
00256        ret_bits = NULL;
00257     }
00258 
00259     return ret_bits;
00260 }
00261 
00262 /* generate bits for the key and iv determination.  if enough bits
00263  * are not generated using PKCS 5, then we need to generate more bits
00264  * based on the extension proposed in PKCS 12
00265  */
00266 static SECItem *
00267 nsspkcs5_PBKDF1Extended(const SECHashObject *hashObj,
00268         NSSPKCS5PBEParameter *pbe_param, SECItem *pwitem, PRBool faulty3DES)
00269 {
00270     SECItem * hash          = NULL;
00271     SECItem * newHash              = NULL;
00272     int       bytes_needed;
00273     int       bytes_available;
00274     
00275     bytes_needed = pbe_param->ivLen + pbe_param->keyLen;
00276     bytes_available = hashObj->length;
00277     
00278     hash = nsspkcs5_PBKDF1(hashObj, &pbe_param->salt, pwitem, 
00279                                           pbe_param->iter, faulty3DES);
00280 
00281     if(hash == NULL) {
00282        return NULL;
00283     }
00284 
00285     if(bytes_needed <= bytes_available) {
00286        return hash;
00287     } 
00288 
00289     newHash = nsspkcs5_PFXPBE(hashObj, pbe_param, hash, bytes_needed);
00290     if (hash != newHash)
00291        SECITEM_FreeItem(hash, PR_TRUE);
00292     return newHash;
00293 }
00294 
00295 #ifdef PBKDF2
00296 
00297 /*
00298  * PBDKDF2 is PKCS #5 v2.0 it's currently not used by NSS
00299  */
00300 /*
00301  * We This is safe because hLen for all our
00302  * HMAC algorithms are multiples of 4.
00303  */
00304 static void
00305 xorbytes(unsigned char *dest, unsigned char *src, int len)
00306 {
00307 #ifdef PARANOIA
00308     while (len--) {
00309        *dest = *dest ^ *src;
00310        dest++;
00311        src++;
00312     }
00313 #else
00314     PRUInt32 dest32 = (PRUInt32 *)dest;
00315     PRUInt32 src32 = (PRUInt32 *)dest;
00316     while (len -= sizeof(PRUInt32)) {
00317        *dest32 = *dest32 ^ *src32;
00318        dest++;
00319        src++;
00320     }
00321 #endif
00322 }
00323 
00324 static SECStatus
00325 nsspkcs5_PBKFD2_F(const SECHashObject *hashobj, SECItem *pwitem, SECItem *salt,
00326                      int iterations, unsigned int i, unsigned char *T)
00327 {
00328     int j;
00329     HMACContext *cx = NULL;
00330     unsigned int hLen = hashObject->length
00331     SECStatus rv = SECFailure;
00332     unsigned char *last = NULL;
00333     int lastLength = salt->len + 4;
00334 
00335     cx=HMAC_Create(hashobj,pwitem->data,pwitem->len,PR_TRUE);
00336     if (cx == NULL) {
00337        goto loser;
00338     }
00339     PORT_Memset(T,0,hLen);
00340     realLastLength= MAX(lastLength,hLen);
00341     last = PORT_Alloc(realLastLength);
00342     if (last == NULL) {
00343        goto loser;
00344     }
00345     PORT_Memcpy(last,salt.data,salt.len);
00346     last[salt->len  ] = (i >> 24) & 0xff;
00347     last[salt->len+1] = (i >> 16) & 0xff;
00348     last[salt->len+2] = (i >> 8) & 0xff;
00349     last[salt->len+3] =    i  & 0xff;
00350 
00351     /* NOTE: we need at least one iteration to return success! */
00352     for (j=0; j < interations; j++) {
00353        rv =HMAC_Begin(cx);
00354        if (rv !=SECSuccess) {
00355           break;
00356        }
00357        HMAC_Update(cx,last,lastLength);
00358        rv =HMAC_Finish(cx,last,&lastLength,hLen);
00359        if (rv !=SECSuccess) {
00360           break;
00361        }
00362        do_xor(T,last,hLen);
00363     }
00364 loser:
00365     if (cx) {
00366        HMAC_DestroyContext(cx, PR_TRUE);
00367     }
00368     if (last) {
00369        PORT_ZFree(last,reaLastLength);
00370     }
00371     return rv;
00372 }
00373 
00374 static SECItem *
00375 nsspkcs5_PBKFD2(const SECHashObject *hashObj, NSSPKCS5PBEParameter *pbe_param, 
00376                                                  SECItem *pwitem)
00377 {
00378     unsigned int dkLen = bytesNeeded;
00379     unsigned int hLen = hashObject->length
00380     unsigned int l = (dkLen+hLen-1) / hLen;
00381     unsigned char *rp;
00382     SECItem *result;
00383     SECItem *salt = pbe_param->salt;
00384     int interations = pbe_param->iter;
00385     int bytesNeeded = pbe_param->keyLen;
00386 
00387     result = SECITEM_AllocItem(NULL,NULL,l*hLen);
00388     if (result == NULL) {
00389        return NULL;
00390     }
00391 
00392     T = PORT_Alloc(hLen);
00393     if (T == NULL) {
00394        goto loser;
00395     }
00396 
00397     for (i=0,rp=results->data; i < l ; i++, rp +=hLen) {
00398        rv = nsspkcs5_PBKFD2_F(hashobj,pwitem,salt,iterations,i,T);
00399        if (rv != SECSuccess) {
00400            break;
00401        }
00402        PORT_Memcpy(rp,T,hLen);
00403     }
00404 
00405 loser:
00406     if (T) {
00407        PORT_ZFree(T);
00408     }
00409     if (rv != SECSuccess) {
00410        SECITEM_FreeITEM(result,PR_TRUE);
00411        result = NULL;
00412     } else {
00413        result->len = dkLen;
00414     }
00415        
00416     return result;
00417 }
00418 #endif
00419 
00420 #define HMAC_BUFFER 64
00421 #define NSSPBE_ROUNDUP(x,y) ((((x)+((y)-1))/(y))*(y))
00422 #define NSSPBE_MIN(x,y) ((x) < (y) ? (x) : (y))
00423 /*
00424  * This is the extended PBE function defined by the final PKCS #12 spec.
00425  */
00426 static SECItem *
00427 nsspkcs5_PKCS12PBE(const SECHashObject *hashObject, 
00428                  NSSPKCS5PBEParameter *pbe_param, SECItem *pwitem, 
00429                  PBEBitGenID bitGenPurpose, unsigned int bytesNeeded)
00430 {
00431     PRArenaPool *arena = NULL;
00432     unsigned int SLen,PLen;
00433     unsigned int hashLength = hashObject->length;
00434     unsigned char *S, *P;
00435     SECItem *A = NULL, B, D, I;
00436     SECItem *salt = &pbe_param->salt;
00437     unsigned int c,i = 0;
00438     unsigned int hashLen;
00439     int iter;
00440     unsigned char *iterBuf;
00441     void *hash = NULL;
00442 
00443     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
00444     if(!arena) {
00445        return NULL;
00446     }
00447 
00448     /* how many hash object lengths are needed */
00449     c = (bytesNeeded + (hashLength-1))/hashLength;
00450 
00451     /* initialize our buffers */
00452     D.len = HMAC_BUFFER;
00453     /* B and D are the same length, use one alloc go get both */
00454     D.data = (unsigned char*)PORT_ArenaZAlloc(arena, D.len*2);
00455     B.len = D.len;
00456     B.data = D.data + D.len;
00457 
00458     /* if all goes well, A will be returned, so don't use our temp arena */
00459     A = SECITEM_AllocItem(NULL,NULL,c*hashLength);
00460     if (A == NULL) {
00461        goto loser;
00462     }
00463     
00464     SLen = NSSPBE_ROUNDUP(salt->len,HMAC_BUFFER);
00465     PLen = NSSPBE_ROUNDUP(pwitem->len,HMAC_BUFFER);
00466     I.len = SLen+PLen;
00467     I.data = (unsigned char*)PORT_ArenaZAlloc(arena, I.len);
00468     if (I.data == NULL) {
00469        goto loser;
00470     }
00471 
00472     /* S & P are only used to initialize I */
00473     S = I.data;
00474     P = S + SLen;
00475 
00476     PORT_Memset(D.data, (char)bitGenPurpose, D.len);
00477     if (SLen) {
00478        for (i=0; i < SLen; i += salt->len) {
00479            PORT_Memcpy(S+i, salt->data, NSSPBE_MIN(SLen-i,salt->len));
00480        }
00481     } 
00482     if (PLen) {
00483        for (i=0; i < PLen; i += pwitem->len) {
00484            PORT_Memcpy(P+i, pwitem->data, NSSPBE_MIN(PLen-i,pwitem->len));
00485        }
00486     } 
00487 
00488     iterBuf = (unsigned char*)PORT_ArenaZAlloc(arena,hashLength);
00489     if (iterBuf == NULL) {
00490        goto loser;
00491     }
00492 
00493     hash = hashObject->create();
00494     if(!hash) {
00495        goto loser;
00496     }
00497     /* calculate the PBE now */
00498     for(i = 0; i < c; i++) {
00499        int Bidx;     /* must be signed or the for loop won't terminate */
00500        unsigned int k, j;
00501        unsigned char *Ai = A->data+i*hashLength;
00502 
00503 
00504        for(iter = 0; iter < pbe_param->iter; iter++) {
00505            hashObject->begin(hash);
00506 
00507            if (iter) {
00508               hashObject->update(hash, iterBuf, hashLen);
00509            } else {
00510               hashObject->update(hash, D.data, D.len);
00511               hashObject->update(hash, I.data, I.len); 
00512            }
00513 
00514            hashObject->end(hash, iterBuf, &hashLen, hashObject->length);
00515            if(hashLen != hashObject->length) {
00516               break;
00517            }
00518        }
00519 
00520        PORT_Memcpy(Ai, iterBuf, hashLength);
00521        for (Bidx = 0; Bidx < B.len; Bidx += hashLength) {
00522            PORT_Memcpy(B.data+Bidx,iterBuf,NSSPBE_MIN(B.len-Bidx,hashLength));
00523        }
00524 
00525        k = I.len/B.len;
00526        for(j = 0; j < k; j++) {
00527            unsigned int q, carryBit;
00528            unsigned char *Ij = I.data + j*B.len;
00529 
00530            /* (Ij = Ij+B+1) */
00531            for (Bidx = (B.len-1), q=1, carryBit=0; Bidx >= 0; Bidx--,q=0) {
00532               q += (unsigned int)Ij[Bidx];
00533               q += (unsigned int)B.data[Bidx];
00534               q += carryBit;
00535 
00536               carryBit = (q > 0xff);
00537               Ij[Bidx] = (unsigned char)(q & 0xff);
00538            }
00539        }
00540     }
00541 loser:
00542     if (hash) {
00543        hashObject->destroy(hash, PR_TRUE);
00544     }
00545     if(arena) {
00546        PORT_FreeArena(arena, PR_TRUE);
00547     }
00548 
00549     if (A) {
00550         /* if i != c, then we didn't complete the loop above and must of failed
00551          * somwhere along the way */
00552         if (i != c) {
00553            SECITEM_ZfreeItem(A,PR_TRUE);
00554            A = NULL;
00555         } else {
00556            A->len = bytesNeeded;
00557         }
00558     }
00559     
00560     return A;
00561 }
00562 
00563 /*
00564  * generate key as per PKCS 5
00565  */
00566 SECItem *
00567 nsspkcs5_ComputeKeyAndIV(NSSPKCS5PBEParameter *pbe_param, SECItem *pwitem,
00568                     SECItem *iv, PRBool faulty3DES)
00569 {
00570     SECItem *hash = NULL, *key = NULL;
00571     const SECHashObject *hashObj;
00572     PRBool getIV = PR_FALSE;
00573 
00574     if((pbe_param == NULL) || (pwitem == NULL)) {
00575        return NULL;
00576     }
00577 
00578     key = SECITEM_AllocItem(NULL,NULL,pbe_param->keyLen);
00579     if (key == NULL) {
00580        return NULL;
00581     }
00582 
00583     if ((pbe_param->ivLen) && (iv->data == NULL)) {
00584        getIV = PR_TRUE;
00585        iv->data = (unsigned char *)PORT_Alloc(pbe_param->ivLen);
00586        if (iv->data == NULL) {
00587            goto loser;
00588        }
00589        iv->len = pbe_param->ivLen;
00590     }
00591 
00592     hashObj = HASH_GetRawHashObject(pbe_param->hashType);
00593     switch (pbe_param->pbeType) {
00594     case NSSPKCS5_PBKDF1:
00595        hash = nsspkcs5_PBKDF1Extended(hashObj,pbe_param,pwitem,faulty3DES);
00596        if (hash == NULL) {
00597            goto loser;
00598        }
00599        PORT_Assert(hash->len >= key->len+iv->len);
00600        if (getIV) {
00601            PORT_Memcpy(iv->data, hash->data+(hash->len - iv->len),iv->len);
00602        }
00603        break;
00604 #ifdef PBKDF2
00605     case NSSPKCS5_PBKDF2:
00606        hash = nsspkcs5_PBKDF2(hashObj,pbe_param,pwitem);
00607        PORT_Assert(!getIV);
00608        break;
00609 #endif
00610     case NSSPKCS5_PKCS12_V2:
00611        if (getIV) {
00612            hash = nsspkcs5_PKCS12PBE(hashObj,pbe_param,pwitem,
00613                                           pbeBitGenCipherIV,iv->len);
00614            if (hash == NULL) {
00615               goto loser;
00616            }
00617            PORT_Memcpy(iv->data,hash->data,iv->len);
00618            SECITEM_ZfreeItem(hash,PR_TRUE);
00619            hash = NULL;
00620        }
00621        hash = nsspkcs5_PKCS12PBE(hashObj,pbe_param,pwitem,
00622                                           pbe_param->keyID,key->len);
00623     default:
00624        break;
00625     }
00626 
00627     if (hash == NULL) {
00628        goto loser;
00629     }
00630 
00631     if (pbe_param->is2KeyDES) {
00632        PORT_Memcpy(key->data, hash->data, (key->len * 2) / 3);
00633        PORT_Memcpy(&(key->data[(key->len  * 2) / 3]), key->data,
00634                   key->len / 3);
00635     } else {
00636        PORT_Memcpy(key->data, hash->data, key->len);
00637     }
00638 
00639     SECITEM_ZfreeItem(hash, PR_TRUE);
00640     return key;
00641 
00642 loser:
00643     if (getIV && iv->data) {
00644        PORT_ZFree(iv->data,iv->len);
00645        iv->data = NULL;
00646     }
00647 
00648     SECITEM_ZfreeItem(key, PR_TRUE);
00649     return NULL;
00650 }
00651 
00652 static SECStatus
00653 nsspkcs5_FillInParam(SECOidTag algorithm, NSSPKCS5PBEParameter *pbe_param)
00654 {
00655     PRBool skipType = PR_FALSE;
00656 
00657     pbe_param->keyLen = 5;
00658     pbe_param->ivLen = 8;
00659     pbe_param->hashType = HASH_AlgSHA1;
00660     pbe_param->pbeType = NSSPKCS5_PBKDF1;
00661     pbe_param->encAlg = SEC_OID_RC2_CBC;
00662     pbe_param->is2KeyDES = PR_FALSE;
00663     switch(algorithm) {
00664     /* DES3 Algorithms */
00665     case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC:
00666        pbe_param->is2KeyDES = PR_TRUE;
00667        /* fall through */
00668     case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC:
00669        pbe_param->pbeType = NSSPKCS5_PKCS12_V2;
00670        /* fall through */
00671     case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC:
00672        pbe_param->keyLen = 24;
00673        pbe_param->encAlg = SEC_OID_DES_EDE3_CBC;
00674        break;
00675 
00676     /* DES Algorithms */
00677     case SEC_OID_PKCS5_PBE_WITH_MD2_AND_DES_CBC:
00678        pbe_param->hashType = HASH_AlgMD2;
00679        goto finish_des;
00680     case SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC:
00681        pbe_param->hashType = HASH_AlgMD5;
00682        /* fall through */
00683     case SEC_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC:
00684 finish_des:
00685        pbe_param->keyLen = 8;
00686        pbe_param->encAlg =  SEC_OID_DES_CBC;
00687        break;
00688 
00689     /* RC2 Algorithms */
00690     case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
00691        pbe_param->keyLen = 16;
00692        /* fall through */
00693     case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
00694        pbe_param->pbeType = NSSPKCS5_PKCS12_V2;
00695        break;
00696     case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
00697        pbe_param->keyLen = 16;
00698        /* fall through */
00699     case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
00700        break;
00701 
00702     /* RC4 algorithms */
00703     case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4:
00704        skipType = PR_TRUE;
00705        /* fall through */
00706     case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4:
00707        pbe_param->keyLen = 16;
00708        /* fall through */
00709     case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4:
00710        if (!skipType) {
00711            pbe_param->pbeType = NSSPKCS5_PKCS12_V2;
00712        }
00713        /* fall through */
00714     case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4:
00715         pbe_param->ivLen = 0;
00716         pbe_param->encAlg =  SEC_OID_RC4;
00717         break;
00718     default:
00719         return SECFailure;
00720     }
00721 
00722     return SECSuccess;
00723 }
00724 
00725 /* decode the algid and generate a PKCS 5 parameter from it
00726  */
00727 NSSPKCS5PBEParameter *
00728 nsspkcs5_NewParam(SECOidTag alg, SECItem *salt, int iterator)
00729 {
00730     PRArenaPool *arena = NULL;
00731     NSSPKCS5PBEParameter *pbe_param = NULL;
00732     SECStatus rv = SECFailure;
00733 
00734     arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
00735     if (arena == NULL)
00736        return NULL;
00737 
00738     /* allocate memory for the parameter */
00739     pbe_param = (NSSPKCS5PBEParameter *)PORT_ArenaZAlloc(arena, 
00740        sizeof(NSSPKCS5PBEParameter));
00741 
00742     if (pbe_param == NULL) {
00743        goto loser;
00744     }
00745 
00746     pbe_param->poolp = arena;
00747 
00748     rv = nsspkcs5_FillInParam(alg, pbe_param);
00749     if (rv != SECSuccess) {
00750        goto loser;
00751     }
00752 
00753     pbe_param->iter = iterator;
00754     if (salt) {
00755        rv = SECITEM_CopyItem(arena,&pbe_param->salt,salt);
00756     }
00757 
00758     /* default key gen */
00759     pbe_param->keyID = pbeBitGenCipherKey;
00760 
00761 loser:
00762     if (rv != SECSuccess) {
00763        PORT_FreeArena(arena, PR_TRUE);
00764        pbe_param = NULL;
00765     }
00766 
00767     return pbe_param;
00768 }
00769 
00770 /* decode the algid and generate a PKCS 5 parameter from it
00771  */
00772 NSSPKCS5PBEParameter *
00773 nsspkcs5_AlgidToParam(SECAlgorithmID *algid)
00774 {
00775     NSSPKCS5PBEParameter *pbe_param = NULL;
00776     SECOidTag algorithm;
00777     SECStatus rv = SECFailure;
00778 
00779     if (algid == NULL) {
00780        return NULL;
00781     }
00782 
00783     algorithm = SECOID_GetAlgorithmTag(algid);
00784     if (algorithm == SEC_OID_UNKNOWN) {
00785        goto loser;
00786     }
00787 
00788     pbe_param = nsspkcs5_NewParam(algorithm, NULL, 1);
00789     if (pbe_param == NULL) {
00790        goto loser;
00791     }
00792 
00793     /* decode parameter */
00794     rv = SECFailure;
00795     switch (pbe_param->pbeType) {
00796     case NSSPKCS5_PBKDF1:
00797        rv = SEC_ASN1DecodeItem(pbe_param->poolp, pbe_param, 
00798            NSSPKCS5PBEParameterTemplate, &algid->parameters);
00799        break;
00800     case NSSPKCS5_PKCS12_V2:
00801        rv = SEC_ASN1DecodeItem(pbe_param->poolp, pbe_param, 
00802               NSSPKCS5PKCS12V2PBEParameterTemplate, &algid->parameters);
00803        break;
00804     case NSSPKCS5_PBKDF2:
00805        break;
00806     }
00807 
00808 loser:
00809     if (rv == SECSuccess) {
00810        pbe_param->iter = DER_GetInteger(&pbe_param->iteration);
00811     } else {
00812        nsspkcs5_DestroyPBEParameter(pbe_param);
00813        pbe_param = NULL;
00814     }
00815 
00816     return pbe_param;
00817 }
00818 
00819 /* destroy a pbe parameter.  it assumes that the parameter was 
00820  * generated using the appropriate create function and therefor
00821  * contains an arena pool.
00822  */
00823 void 
00824 nsspkcs5_DestroyPBEParameter(NSSPKCS5PBEParameter *pbe_param)
00825 {
00826     if (pbe_param != NULL) {
00827        PORT_FreeArena(pbe_param->poolp, PR_FALSE);
00828     }
00829 }
00830 
00831 
00832 /* crypto routines */
00833 /* perform DES encryption and decryption.  these routines are called
00834  * by nsspkcs5_CipherData.  In the case of an error, NULL is returned.
00835  */
00836 static SECItem *
00837 sec_pkcs5_des(SECItem *key, SECItem *iv, SECItem *src, PRBool triple_des, 
00838                                                         PRBool encrypt)
00839 {
00840     SECItem *dest;
00841     SECItem *dup_src;
00842     SECStatus rv = SECFailure;
00843     int pad;
00844 
00845     if((src == NULL) || (key == NULL) || (iv == NULL))
00846        return NULL;
00847 
00848     dup_src = SECITEM_DupItem(src);
00849     if(dup_src == NULL) {
00850        return NULL;
00851     }
00852 
00853     if(encrypt != PR_FALSE) {
00854        void *dummy;
00855 
00856        dummy = DES_PadBuffer(NULL, dup_src->data, 
00857            dup_src->len, &dup_src->len);
00858        if(dummy == NULL) {
00859            SECITEM_FreeItem(dup_src, PR_TRUE);
00860            return NULL;
00861        }
00862        dup_src->data = (unsigned char*)dummy;
00863     }
00864 
00865     dest = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
00866     if(dest != NULL) {
00867        /* allocate with over flow */
00868        dest->data = (unsigned char *)PORT_ZAlloc(dup_src->len + 64);
00869        if(dest->data != NULL) {
00870            DESContext *ctxt;
00871            ctxt = DES_CreateContext(key->data, iv->data, 
00872                      (triple_des ? NSS_DES_EDE3_CBC : NSS_DES_CBC), 
00873                      encrypt);
00874        
00875            if(ctxt != NULL) {
00876               rv = (encrypt ? DES_Encrypt : DES_Decrypt)(
00877                      ctxt, dest->data, &dest->len,
00878                      dup_src->len + 64, dup_src->data, dup_src->len);
00879 
00880               /* remove padding -- assumes 64 bit blocks */
00881               if((encrypt == PR_FALSE) && (rv == SECSuccess)) {
00882                   pad = dest->data[dest->len-1];
00883                   if((pad > 0) && (pad <= 8)) {
00884                      if(dest->data[dest->len-pad] != pad) {
00885                          rv = SECFailure;
00886                          PORT_SetError(SEC_ERROR_BAD_PASSWORD);
00887                      } else {
00888                          dest->len -= pad;
00889                      }
00890                   } else {
00891                      rv = SECFailure;
00892                      PORT_SetError(SEC_ERROR_BAD_PASSWORD);
00893                   }
00894               }
00895               DES_DestroyContext(ctxt, PR_TRUE);
00896            }
00897        }
00898     }
00899 
00900     if(rv == SECFailure) {
00901        if(dest != NULL) {
00902            SECITEM_FreeItem(dest, PR_TRUE);
00903        }
00904        dest = NULL;
00905     }
00906 
00907     if(dup_src != NULL) {
00908        SECITEM_FreeItem(dup_src, PR_TRUE);
00909     }
00910 
00911     return dest;
00912 }
00913 
00914 /* perform rc2 encryption/decryption if an error occurs, NULL is returned
00915  */
00916 static SECItem *
00917 sec_pkcs5_rc2(SECItem *key, SECItem *iv, SECItem *src, PRBool dummy, 
00918                                                         PRBool encrypt)
00919 {
00920     SECItem *dest;
00921     SECItem *dup_src;
00922     SECStatus rv = SECFailure;
00923     int pad;
00924 
00925     if((src == NULL) || (key == NULL) || (iv == NULL)) {
00926        return NULL;
00927     }
00928 
00929     dup_src = SECITEM_DupItem(src);
00930     if(dup_src == NULL) {
00931        return NULL;
00932     }
00933 
00934     if(encrypt != PR_FALSE) {
00935        void *dummy;
00936 
00937        dummy = DES_PadBuffer(NULL, dup_src->data, 
00938                            dup_src->len, &dup_src->len);
00939        if(dummy == NULL) {
00940            SECITEM_FreeItem(dup_src, PR_TRUE);
00941            return NULL;
00942        }
00943        dup_src->data = (unsigned char*)dummy;
00944     }
00945 
00946     dest = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
00947     if(dest != NULL) {
00948        dest->data = (unsigned char *)PORT_ZAlloc(dup_src->len + 64);
00949        if(dest->data != NULL) {
00950            RC2Context *ctxt;
00951 
00952            ctxt = RC2_CreateContext(key->data, key->len, iv->data,
00953                                           NSS_RC2_CBC, key->len);
00954 
00955            if(ctxt != NULL) {
00956               rv = (encrypt ? RC2_Encrypt: RC2_Decrypt)(
00957                      ctxt, dest->data, &dest->len,
00958                      dup_src->len + 64, dup_src->data, dup_src->len);
00959 
00960               /* assumes 8 byte blocks  -- remove padding */   
00961               if((rv == SECSuccess) && (encrypt != PR_TRUE)) {
00962                   pad = dest->data[dest->len-1];
00963                   if((pad > 0) && (pad <= 8)) {
00964                      if(dest->data[dest->len-pad] != pad) {
00965                          PORT_SetError(SEC_ERROR_BAD_PASSWORD);
00966                          rv = SECFailure;
00967                      } else {
00968                          dest->len -= pad;
00969                      }
00970                   } else {
00971                      PORT_SetError(SEC_ERROR_BAD_PASSWORD);
00972                      rv = SECFailure;
00973                   }
00974               }
00975 
00976            }
00977        }
00978     }
00979 
00980     if((rv != SECSuccess) && (dest != NULL)) {
00981        SECITEM_FreeItem(dest, PR_TRUE);
00982        dest = NULL;
00983     }
00984 
00985     if(dup_src != NULL) {
00986        SECITEM_FreeItem(dup_src, PR_TRUE);
00987     }
00988 
00989     return dest;
00990 }
00991 
00992 /* perform rc4 encryption and decryption */
00993 static SECItem *
00994 sec_pkcs5_rc4(SECItem *key, SECItem *iv, SECItem *src, PRBool dummy_op,
00995                                                          PRBool encrypt)
00996 {
00997     SECItem *dest;
00998     SECStatus rv = SECFailure;
00999 
01000     if((src == NULL) || (key == NULL) || (iv == NULL)) {
01001        return NULL;
01002     }
01003 
01004     dest = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
01005     if(dest != NULL) {
01006        dest->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) *
01007            (src->len + 64));
01008        if(dest->data != NULL) {
01009            RC4Context *ctxt;
01010 
01011            ctxt = RC4_CreateContext(key->data, key->len);
01012            if(ctxt) { 
01013               rv = (encrypt ? RC4_Encrypt : RC4_Decrypt)(
01014                             ctxt, dest->data, &dest->len,
01015                             src->len + 64, src->data, src->len);
01016               RC4_DestroyContext(ctxt, PR_TRUE);
01017            }
01018        }
01019     }
01020 
01021     if((rv != SECSuccess) && (dest)) {
01022        SECITEM_FreeItem(dest, PR_TRUE);
01023        dest = NULL;
01024     }
01025 
01026     return dest;
01027 }
01028 /* function pointer template for crypto functions */
01029 typedef SECItem *(* pkcs5_crypto_func)(SECItem *key, SECItem *iv,
01030                                          SECItem *src, PRBool op1, PRBool op2);
01031 
01032 /* performs the cipher operation on the src and returns the result.
01033  * if an error occurs, NULL is returned. 
01034  *
01035  * a null length password is allowed.  this corresponds to encrypting
01036  * the data with ust the salt.
01037  */
01038 /* change this to use PKCS 11? */
01039 SECItem *
01040 nsspkcs5_CipherData(NSSPKCS5PBEParameter *pbe_param, SECItem *pwitem, 
01041                   SECItem *src, PRBool encrypt, PRBool *update)
01042 {
01043     SECItem *key = NULL, iv;
01044     SECItem *dest = NULL;
01045     PRBool tripleDES = PR_TRUE;
01046     pkcs5_crypto_func cryptof;
01047 
01048     iv.data = NULL;
01049 
01050     if (update) { 
01051         *update = PR_FALSE;
01052     }
01053 
01054     if ((pwitem == NULL) || (src == NULL)) {
01055        return NULL;
01056     }
01057 
01058     /* get key, and iv */
01059     key = nsspkcs5_ComputeKeyAndIV(pbe_param, pwitem, &iv, PR_FALSE);
01060     if(key == NULL) {
01061        return NULL;
01062     }
01063 
01064     switch(pbe_param->encAlg) {
01065     case SEC_OID_DES_EDE3_CBC:
01066        cryptof = sec_pkcs5_des;
01067        tripleDES = PR_TRUE;
01068        break;
01069     case SEC_OID_DES_CBC:
01070        cryptof = sec_pkcs5_des;
01071        tripleDES = PR_FALSE;
01072        break;
01073     case SEC_OID_RC2_CBC:
01074        cryptof = sec_pkcs5_rc2;
01075        break;
01076     case SEC_OID_RC4:
01077        cryptof = sec_pkcs5_rc4;
01078        break;
01079     default:
01080        cryptof = NULL;
01081        break;
01082     }
01083 
01084     if (cryptof == NULL) {
01085        goto loser;
01086     }
01087 
01088     dest = (*cryptof)(key, &iv, src, tripleDES, encrypt);
01089     /* 
01090      * it's possible for some keys and keydb's to claim to
01091      * be triple des when they're really des. In this case
01092      * we simply try des. If des works we set the update flag
01093      * so the key db knows it needs to update all it's entries.
01094      *  The case can only happen on decrypted of a 
01095      *  SEC_OID_DES_EDE3_CBD.
01096      */
01097     if ((dest == NULL) && (encrypt == PR_FALSE) && 
01098                             (pbe_param->encAlg == SEC_OID_DES_EDE3_CBC)) {
01099        dest = (*cryptof)(key, &iv, src, PR_FALSE, encrypt);
01100        if (update && (dest != NULL)) *update = PR_TRUE;
01101     }
01102 
01103 loser:
01104     if (key != NULL) {
01105        SECITEM_ZfreeItem(key, PR_TRUE);
01106     }
01107     if (iv.data != NULL) {
01108        SECITEM_ZfreeItem(&iv, PR_FALSE);
01109     }
01110 
01111     return dest;
01112 }
01113 
01114 /* creates a algorithm ID containing the PBE algorithm and appropriate
01115  * parameters.  the required parameter is the algorithm.  if salt is
01116  * not specified, it is generated randomly.  if IV is specified, it overrides
01117  * the PKCS 5 generation of the IV.  
01118  *
01119  * the returned SECAlgorithmID should be destroyed using 
01120  * SECOID_DestroyAlgorithmID
01121  */
01122 SECAlgorithmID *
01123 nsspkcs5_CreateAlgorithmID(PRArenaPool *arena, SECOidTag algorithm, 
01124                                    NSSPKCS5PBEParameter *pbe_param)
01125 {
01126     SECAlgorithmID *algid, *ret_algid = NULL;
01127     SECItem der_param;
01128     SECStatus rv = SECFailure;
01129     void *dummy = NULL;
01130 
01131     if (arena == NULL) {
01132        return NULL;
01133     }
01134 
01135     der_param.data = NULL;
01136     der_param.len = 0;
01137 
01138     /* generate the algorithm id */
01139     algid = (SECAlgorithmID *)PORT_ArenaZAlloc(arena, sizeof(SECAlgorithmID));
01140     if (algid == NULL) {
01141        goto loser;
01142     }
01143 
01144     if (pbe_param->iteration.data == NULL) {
01145        dummy = SEC_ASN1EncodeInteger(pbe_param->poolp,&pbe_param->iteration,
01146                                                         pbe_param->iter);
01147        if (dummy == NULL) {
01148            goto loser;
01149        }
01150     }
01151     switch (pbe_param->pbeType) {
01152     case NSSPKCS5_PBKDF1:
01153        dummy = SEC_ASN1EncodeItem(arena, &der_param, pbe_param,
01154                                    NSSPKCS5PBEParameterTemplate);
01155        break;
01156     case NSSPKCS5_PKCS12_V2:
01157        dummy = SEC_ASN1EncodeItem(arena, &der_param, pbe_param,
01158                                    NSSPKCS5PKCS12V2PBEParameterTemplate);
01159        break;
01160     default:
01161        break;
01162     }
01163 
01164     if (dummy == NULL) {
01165        goto loser;
01166     }
01167        
01168     rv = SECOID_SetAlgorithmID(arena, algid, algorithm, &der_param);
01169     if (rv != SECSuccess) {
01170        goto loser;
01171     }
01172 
01173     ret_algid = (SECAlgorithmID *)PORT_ZAlloc(sizeof(SECAlgorithmID));
01174     if (ret_algid == NULL) {
01175        goto loser;
01176     }
01177 
01178     rv = SECOID_CopyAlgorithmID(NULL, ret_algid, algid);
01179     if (rv != SECSuccess) {
01180        SECOID_DestroyAlgorithmID(ret_algid, PR_TRUE);
01181        ret_algid = NULL;
01182     }
01183 
01184 loser: 
01185 
01186     return ret_algid;
01187 }