sl@0: /* sl@0: * Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: * All rights reserved. sl@0: * This component and the accompanying materials are made available sl@0: * under the terms of the License "Eclipse Public License v1.0" sl@0: * which accompanies this distribution, and is available sl@0: * at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: * sl@0: * Initial Contributors: sl@0: * Nokia Corporation - initial contribution. sl@0: * sl@0: * Contributors: sl@0: * sl@0: * Description: sl@0: * sl@0: */ sl@0: sl@0: sl@0: #include sl@0: #include sl@0: #include "pkcs12kdf.h" sl@0: sl@0: sl@0: EXPORT_C HBufC8* PKCS12KDF::GeneratePasswordLC(const TDesC& aDes) sl@0: /** sl@0: Convert the supplied string to a byte string, as described sl@0: in SB.1 of the PKCS 12 v1.0. sl@0: sl@0: Each character is converted to a big endian two-byte value, sl@0: and a terminating NULL character is appended to the end. sl@0: sl@0: @param aDes String to use as password. sl@0: */ sl@0: { sl@0: const TInt len = aDes.Length(); sl@0: HBufC8* pwdBytes = HBufC8::NewMaxLC((len + 1) * 2); sl@0: TPtr8 pbDes = pwdBytes->Des(); sl@0: sl@0: TInt i = 0; sl@0: while (i < len) sl@0: { sl@0: TUint16 ch = aDes[i]; sl@0: pbDes[i * 2] = ch >> 8; sl@0: pbDes[(i * 2) + 1] = ch; sl@0: ++i; sl@0: } sl@0: pbDes[i * 2] = pbDes[(i * 2) + 1] = 0; sl@0: sl@0: return pwdBytes; sl@0: } sl@0: sl@0: static TInt CeilDiv(TInt aNumerator, TInt aDenominator) sl@0: /** sl@0: Utility function returns ceil(aNumerator / aDenominator). sl@0: sl@0: @param aNumerator The numerator. sl@0: @param aDenominator Denominator, which cannot be zero. sl@0: @return ceil(aNumerator / aDenominator) sl@0: */ sl@0: { sl@0: TInt result = aNumerator / aDenominator; sl@0: if ((aNumerator % aDenominator) > 0) sl@0: ++result; sl@0: return result; sl@0: } sl@0: sl@0: EXPORT_C void PKCS12KDF::DeriveKeyL( sl@0: TDes8& aKey, TIDByteType aIDType, sl@0: const TDesC8& aPasswd, const TDesC8& aSalt, const TUint aIterations) sl@0: /** sl@0: Generate a key for the supplied password and salt. sl@0: This implementation uses SHA1 as the hashing algorithm. sl@0: sl@0: @param aKey Descriptor which will hold key. On entry sl@0: its length must be set to the expected key length. sl@0: @param aIDType Whether this function is being called to generate sl@0: an (en|de)cryption key, an initialization vector, sl@0: or a key for MAC-ing. See SB.3 of spec. sl@0: @param aPasswd Password string. To comply with PKCS#12 spec, sl@0: this must have 2-byte big-endian characters with sl@0: a terminating null character. sl@0: @param aSalt Used with aPasswd to generate key. sl@0: @param aIterations Number of times to call the hash function for sl@0: each block in the key. sl@0: sl@0: @panic PKCS#12 16 Password is empty (debug only.) sl@0: @panic PKCS#12 17 Password does not contain an even number of bytes, sl@0: and so can't use double-byte characters (debug only.) sl@0: @panic PKCS#12 18 The final two-byte character is not a null terminator, sl@0: or a null terminator occurs before the end (debug only.) sl@0: */ sl@0: { sl@0: __ASSERT_DEBUG(aPasswd.Length() >= 2, Panic(EDKEmptyPswd)); sl@0: __ASSERT_DEBUG((aPasswd.Length() % 2) == 0, Panic(EDKOddPswdByteCount)); sl@0: TInt useCharCount = aPasswd.Length() / 2; sl@0: TPtrC16 pswd16(reinterpret_cast(aPasswd.Ptr()), useCharCount); sl@0: TInt nullPos = pswd16.Locate(L'\0'); sl@0: __ASSERT_DEBUG(nullPos == (useCharCount - 1), Panic(EDKBadNullTerminator)); sl@0: sl@0: // use the same notation as the standard sl@0: const TUint8 ID = static_cast(aIDType); sl@0: const TInt u = 160; // chaining variable length for SHA-1 sl@0: const TInt v = 512; // message input length for SHA-1 sl@0: const TInt n = aKey.Length() * 8; // number of bits required in key sl@0: const TInt p = aPasswd.Length(); sl@0: const TInt s = aSalt.Length(); sl@0: const TInt r = aIterations; sl@0: sl@0: // (numbered steps are from the standard) sl@0: // 1. Construct a string, D (the "diversifier"), by concatenating sl@0: // v/8 copies of ID. sl@0: const TInt D_LEN = v / 8; sl@0: HBufC8* D_ = HBufC8::NewMaxLC(D_LEN); sl@0: TPtr8 D = D_->Des(); sl@0: D.Fill(ID); sl@0: sl@0: // 2. Concatenate copies of the salt together to create a string S sl@0: // of length v * ceil(s/v) bits (the final copy of the salt may be sl@0: // truncated to create S). Note that if the salt is the empty string, sl@0: // then so is S. sl@0: const TInt S_OVER_V_CEIL = CeilDiv(s, v); sl@0: const TInt S_LEN = (v * S_OVER_V_CEIL) / 8; sl@0: HBufC8* S_ = HBufC8::NewMaxLC(S_LEN); sl@0: TPtr8 S = S_->Des(); sl@0: S.Repeat(aSalt); sl@0: sl@0: // 3. Concatenate copies of the password together to create a string P sl@0: // of length v * ceil(p/v) bits (the final copy of the password may be sl@0: // truncated to create P). Note that if the password is the empty string sl@0: // then so is P. sl@0: const TInt P_OVER_V_CEIL = CeilDiv(p, v); sl@0: const TInt P_LEN = (v * P_OVER_V_CEIL) / 8; sl@0: HBufC8* P_ = HBufC8::NewMaxLC(P_LEN); sl@0: TPtr8 P = P_->Des(); sl@0: P.Repeat(aPasswd); sl@0: sl@0: // 4. Set I=S||P to be the concatenation of S and P. sl@0: const TInt I_LEN = S_LEN + P_LEN; sl@0: HBufC8* I_ = HBufC8::NewLC(I_LEN); sl@0: TPtr8 I = I_->Des(); sl@0: I.Copy(S); sl@0: I.Append(P); sl@0: sl@0: // 5. Set c=ceil(n/u). sl@0: const TInt c = CeilDiv(n, u); sl@0: sl@0: // ahead 7: allocate result buffer A sl@0: // (Each Ai has SHA1_HASH bytes.) sl@0: HBufC8* A_ = HBufC8::NewLC(c * SHA1_HASH); sl@0: TPtr8 A = A_->Des(); sl@0: sl@0: // 6. For i=1, 2, ..., c, do the following sl@0: sl@0: // pre-allocate SHA1 object, DI, and B buffers sl@0: CSHA1* sha1 = CSHA1::NewL(); sl@0: CleanupStack::PushL(sha1); sl@0: sl@0: const TInt DI_LEN = D_LEN + I_LEN; sl@0: HBufC8* DI_ = HBufC8::NewLC(DI_LEN); sl@0: TPtr8 DI = DI_->Des(); sl@0: sl@0: const TInt B_LEN = v / 8; sl@0: HBufC8* B_ = HBufC8::NewMaxLC(B_LEN); sl@0: TPtr8 B = B_->Des(); sl@0: sl@0: for (TInt i = 1; i <= c; ++i) sl@0: { sl@0: // 6a) Set Ai = H^r(D||I). (i.e. the rth hash of D||I, sl@0: // H(H(H(...H(D||I)))) sl@0: DI.Copy(D); sl@0: DI.Append(I); sl@0: sl@0: sha1->Reset(); sl@0: TBuf8 Ai(sha1->Final(DI)); sl@0: sl@0: for (TInt iterCount = 2; iterCount <= r; ++iterCount) sl@0: { sl@0: Ai.Copy(sha1->Final(Ai)); sl@0: } sl@0: sl@0: // 6b) Concatenate copies of Ai to create a string B of length sl@0: // v bits (the final copy of Ai may be truncated to create B). sl@0: B.Repeat(Ai); sl@0: sl@0: // 6c) Treating I as a concatenation I0, I1, ..., Ik-1 of sl@0: // v-bit blocks, where k=ceil(s/v)+ceil(p/v), modify I by sl@0: // setting Ij=(Ij+B+1) mod 2^v for each j. sl@0: sl@0: const TInt k = S_OVER_V_CEIL + P_OVER_V_CEIL; sl@0: for (TInt j = 0; j < k; ++j) sl@0: { sl@0: TPtr8 section = I.MidTPtr((v/8) * j, v/8); sl@0: Process6cL(section, B, v); sl@0: } sl@0: sl@0: // 7. Concatenate A1, A2, ..., Ac together to form a pseudo-random sl@0: // bit string, A. sl@0: A.Append(Ai); sl@0: sl@0: // stop building A if already have enough bits for key sl@0: if (A.Length() >= n / 8) sl@0: break; sl@0: } sl@0: sl@0: // Use the first n bits of A as the output of this entire process. sl@0: aKey.Copy(A.Left(n / 8)); sl@0: sl@0: CleanupStack::PopAndDestroy(8, D_); // B_, DI_, sha1, A_, I_, P_, S_, D_ sl@0: } sl@0: sl@0: void PKCS12KDF::Process6cL(TDes8& Ij, const TDesC8& B, TInt v) sl@0: /** sl@0: Helper function for DeriveKeyL modifies part of I, sl@0: as described in step 6c of SB.2. sl@0: sl@0: @param Ij Section of I (S || P). sl@0: @param B rth hash of D || I. sl@0: @param v Number of bits to preserve in result. sl@0: */ sl@0: { sl@0: // 6c) Treating I as a concatenation I0, I1, ..., Ik-1 of sl@0: // v-bit blocks, where k=ceil(s/v)+ceil(p/v), modify I by sl@0: // setting Ij=(Ij+B+1) mod 2^v for each j. sl@0: sl@0: RInteger RI_Ij = RInteger::NewL(Ij); sl@0: TCleanupItem ciIj = RI_Ij; sl@0: CleanupStack::PushL(ciIj); sl@0: sl@0: RInteger RI_B = RInteger::NewL(B); sl@0: TCleanupItem ciB = RI_B; sl@0: CleanupStack::PushL(ciB); sl@0: sl@0: // these additions can leave sl@0: RI_Ij += RI_B; sl@0: RI_Ij += 1; sl@0: sl@0: HBufC8* result = RI_Ij.BufferLC(); sl@0: sl@0: Ij.Zero(); sl@0: TInt resultLen = result->Length(); sl@0: sl@0: TInt bytesToPreserve = v / 8; sl@0: TInt leadingZeroes = bytesToPreserve - resultLen; sl@0: if (leadingZeroes <= 0) sl@0: Ij.Copy(result->Right(bytesToPreserve)); sl@0: else sl@0: { sl@0: Ij.FillZ(leadingZeroes); sl@0: Ij.Append(*result); sl@0: } sl@0: sl@0: CleanupStack::PopAndDestroy(3, &RI_Ij); // result, ciB, ciIj sl@0: } sl@0: sl@0: #ifdef _DEBUG sl@0: sl@0: void PKCS12KDF::Panic(PKCS12KDF::TPanic aPanic) sl@0: /** sl@0: This function is used in debug builds to halt sl@0: the current thread when a logic error is detected. sl@0: sl@0: The current thread is panicked with category "PKCS12KDF" sl@0: and the supplied reason. sl@0: sl@0: @param aPanic Converted to numeric value and sl@0: used for the panic reason. sl@0: */ sl@0: { sl@0: _LIT(KPanicCat, "PKCS12KDF"); sl@0: User::Panic(KPanicCat, aPanic); sl@0: } sl@0: sl@0: #endif sl@0: