|
1 /* |
|
2 * Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of the License "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 #include <hash.h> |
|
20 #include <bigint.h> |
|
21 #include "pkcs12kdf.h" |
|
22 |
|
23 |
|
24 EXPORT_C HBufC8* PKCS12KDF::GeneratePasswordLC(const TDesC& aDes) |
|
25 /** |
|
26 Convert the supplied string to a byte string, as described |
|
27 in SB.1 of the PKCS 12 v1.0. |
|
28 |
|
29 Each character is converted to a big endian two-byte value, |
|
30 and a terminating NULL character is appended to the end. |
|
31 |
|
32 @param aDes String to use as password. |
|
33 */ |
|
34 { |
|
35 const TInt len = aDes.Length(); |
|
36 HBufC8* pwdBytes = HBufC8::NewMaxLC((len + 1) * 2); |
|
37 TPtr8 pbDes = pwdBytes->Des(); |
|
38 |
|
39 TInt i = 0; |
|
40 while (i < len) |
|
41 { |
|
42 TUint16 ch = aDes[i]; |
|
43 pbDes[i * 2] = ch >> 8; |
|
44 pbDes[(i * 2) + 1] = ch; |
|
45 ++i; |
|
46 } |
|
47 pbDes[i * 2] = pbDes[(i * 2) + 1] = 0; |
|
48 |
|
49 return pwdBytes; |
|
50 } |
|
51 |
|
52 static TInt CeilDiv(TInt aNumerator, TInt aDenominator) |
|
53 /** |
|
54 Utility function returns ceil(aNumerator / aDenominator). |
|
55 |
|
56 @param aNumerator The numerator. |
|
57 @param aDenominator Denominator, which cannot be zero. |
|
58 @return ceil(aNumerator / aDenominator) |
|
59 */ |
|
60 { |
|
61 TInt result = aNumerator / aDenominator; |
|
62 if ((aNumerator % aDenominator) > 0) |
|
63 ++result; |
|
64 return result; |
|
65 } |
|
66 |
|
67 EXPORT_C void PKCS12KDF::DeriveKeyL( |
|
68 TDes8& aKey, TIDByteType aIDType, |
|
69 const TDesC8& aPasswd, const TDesC8& aSalt, const TUint aIterations) |
|
70 /** |
|
71 Generate a key for the supplied password and salt. |
|
72 This implementation uses SHA1 as the hashing algorithm. |
|
73 |
|
74 @param aKey Descriptor which will hold key. On entry |
|
75 its length must be set to the expected key length. |
|
76 @param aIDType Whether this function is being called to generate |
|
77 an (en|de)cryption key, an initialization vector, |
|
78 or a key for MAC-ing. See SB.3 of spec. |
|
79 @param aPasswd Password string. To comply with PKCS#12 spec, |
|
80 this must have 2-byte big-endian characters with |
|
81 a terminating null character. |
|
82 @param aSalt Used with aPasswd to generate key. |
|
83 @param aIterations Number of times to call the hash function for |
|
84 each block in the key. |
|
85 |
|
86 @panic PKCS#12 16 Password is empty (debug only.) |
|
87 @panic PKCS#12 17 Password does not contain an even number of bytes, |
|
88 and so can't use double-byte characters (debug only.) |
|
89 @panic PKCS#12 18 The final two-byte character is not a null terminator, |
|
90 or a null terminator occurs before the end (debug only.) |
|
91 */ |
|
92 { |
|
93 __ASSERT_DEBUG(aPasswd.Length() >= 2, Panic(EDKEmptyPswd)); |
|
94 __ASSERT_DEBUG((aPasswd.Length() % 2) == 0, Panic(EDKOddPswdByteCount)); |
|
95 TInt useCharCount = aPasswd.Length() / 2; |
|
96 TPtrC16 pswd16(reinterpret_cast<const TUint16*>(aPasswd.Ptr()), useCharCount); |
|
97 TInt nullPos = pswd16.Locate(L'\0'); |
|
98 __ASSERT_DEBUG(nullPos == (useCharCount - 1), Panic(EDKBadNullTerminator)); |
|
99 |
|
100 // use the same notation as the standard |
|
101 const TUint8 ID = static_cast<TUint8>(aIDType); |
|
102 const TInt u = 160; // chaining variable length for SHA-1 |
|
103 const TInt v = 512; // message input length for SHA-1 |
|
104 const TInt n = aKey.Length() * 8; // number of bits required in key |
|
105 const TInt p = aPasswd.Length(); |
|
106 const TInt s = aSalt.Length(); |
|
107 const TInt r = aIterations; |
|
108 |
|
109 // (numbered steps are from the standard) |
|
110 // 1. Construct a string, D (the "diversifier"), by concatenating |
|
111 // v/8 copies of ID. |
|
112 const TInt D_LEN = v / 8; |
|
113 HBufC8* D_ = HBufC8::NewMaxLC(D_LEN); |
|
114 TPtr8 D = D_->Des(); |
|
115 D.Fill(ID); |
|
116 |
|
117 // 2. Concatenate copies of the salt together to create a string S |
|
118 // of length v * ceil(s/v) bits (the final copy of the salt may be |
|
119 // truncated to create S). Note that if the salt is the empty string, |
|
120 // then so is S. |
|
121 const TInt S_OVER_V_CEIL = CeilDiv(s, v); |
|
122 const TInt S_LEN = (v * S_OVER_V_CEIL) / 8; |
|
123 HBufC8* S_ = HBufC8::NewMaxLC(S_LEN); |
|
124 TPtr8 S = S_->Des(); |
|
125 S.Repeat(aSalt); |
|
126 |
|
127 // 3. Concatenate copies of the password together to create a string P |
|
128 // of length v * ceil(p/v) bits (the final copy of the password may be |
|
129 // truncated to create P). Note that if the password is the empty string |
|
130 // then so is P. |
|
131 const TInt P_OVER_V_CEIL = CeilDiv(p, v); |
|
132 const TInt P_LEN = (v * P_OVER_V_CEIL) / 8; |
|
133 HBufC8* P_ = HBufC8::NewMaxLC(P_LEN); |
|
134 TPtr8 P = P_->Des(); |
|
135 P.Repeat(aPasswd); |
|
136 |
|
137 // 4. Set I=S||P to be the concatenation of S and P. |
|
138 const TInt I_LEN = S_LEN + P_LEN; |
|
139 HBufC8* I_ = HBufC8::NewLC(I_LEN); |
|
140 TPtr8 I = I_->Des(); |
|
141 I.Copy(S); |
|
142 I.Append(P); |
|
143 |
|
144 // 5. Set c=ceil(n/u). |
|
145 const TInt c = CeilDiv(n, u); |
|
146 |
|
147 // ahead 7: allocate result buffer A |
|
148 // (Each Ai has SHA1_HASH bytes.) |
|
149 HBufC8* A_ = HBufC8::NewLC(c * SHA1_HASH); |
|
150 TPtr8 A = A_->Des(); |
|
151 |
|
152 // 6. For i=1, 2, ..., c, do the following |
|
153 |
|
154 // pre-allocate SHA1 object, DI, and B buffers |
|
155 CSHA1* sha1 = CSHA1::NewL(); |
|
156 CleanupStack::PushL(sha1); |
|
157 |
|
158 const TInt DI_LEN = D_LEN + I_LEN; |
|
159 HBufC8* DI_ = HBufC8::NewLC(DI_LEN); |
|
160 TPtr8 DI = DI_->Des(); |
|
161 |
|
162 const TInt B_LEN = v / 8; |
|
163 HBufC8* B_ = HBufC8::NewMaxLC(B_LEN); |
|
164 TPtr8 B = B_->Des(); |
|
165 |
|
166 for (TInt i = 1; i <= c; ++i) |
|
167 { |
|
168 // 6a) Set Ai = H^r(D||I). (i.e. the rth hash of D||I, |
|
169 // H(H(H(...H(D||I)))) |
|
170 DI.Copy(D); |
|
171 DI.Append(I); |
|
172 |
|
173 sha1->Reset(); |
|
174 TBuf8<SHA1_HASH> Ai(sha1->Final(DI)); |
|
175 |
|
176 for (TInt iterCount = 2; iterCount <= r; ++iterCount) |
|
177 { |
|
178 Ai.Copy(sha1->Final(Ai)); |
|
179 } |
|
180 |
|
181 // 6b) Concatenate copies of Ai to create a string B of length |
|
182 // v bits (the final copy of Ai may be truncated to create B). |
|
183 B.Repeat(Ai); |
|
184 |
|
185 // 6c) Treating I as a concatenation I0, I1, ..., Ik-1 of |
|
186 // v-bit blocks, where k=ceil(s/v)+ceil(p/v), modify I by |
|
187 // setting Ij=(Ij+B+1) mod 2^v for each j. |
|
188 |
|
189 const TInt k = S_OVER_V_CEIL + P_OVER_V_CEIL; |
|
190 for (TInt j = 0; j < k; ++j) |
|
191 { |
|
192 TPtr8 section = I.MidTPtr((v/8) * j, v/8); |
|
193 Process6cL(section, B, v); |
|
194 } |
|
195 |
|
196 // 7. Concatenate A1, A2, ..., Ac together to form a pseudo-random |
|
197 // bit string, A. |
|
198 A.Append(Ai); |
|
199 |
|
200 // stop building A if already have enough bits for key |
|
201 if (A.Length() >= n / 8) |
|
202 break; |
|
203 } |
|
204 |
|
205 // Use the first n bits of A as the output of this entire process. |
|
206 aKey.Copy(A.Left(n / 8)); |
|
207 |
|
208 CleanupStack::PopAndDestroy(8, D_); // B_, DI_, sha1, A_, I_, P_, S_, D_ |
|
209 } |
|
210 |
|
211 void PKCS12KDF::Process6cL(TDes8& Ij, const TDesC8& B, TInt v) |
|
212 /** |
|
213 Helper function for DeriveKeyL modifies part of I, |
|
214 as described in step 6c of SB.2. |
|
215 |
|
216 @param Ij Section of I (S || P). |
|
217 @param B rth hash of D || I. |
|
218 @param v Number of bits to preserve in result. |
|
219 */ |
|
220 { |
|
221 // 6c) Treating I as a concatenation I0, I1, ..., Ik-1 of |
|
222 // v-bit blocks, where k=ceil(s/v)+ceil(p/v), modify I by |
|
223 // setting Ij=(Ij+B+1) mod 2^v for each j. |
|
224 |
|
225 RInteger RI_Ij = RInteger::NewL(Ij); |
|
226 TCleanupItem ciIj = RI_Ij; |
|
227 CleanupStack::PushL(ciIj); |
|
228 |
|
229 RInteger RI_B = RInteger::NewL(B); |
|
230 TCleanupItem ciB = RI_B; |
|
231 CleanupStack::PushL(ciB); |
|
232 |
|
233 // these additions can leave |
|
234 RI_Ij += RI_B; |
|
235 RI_Ij += 1; |
|
236 |
|
237 HBufC8* result = RI_Ij.BufferLC(); |
|
238 |
|
239 Ij.Zero(); |
|
240 TInt resultLen = result->Length(); |
|
241 |
|
242 TInt bytesToPreserve = v / 8; |
|
243 TInt leadingZeroes = bytesToPreserve - resultLen; |
|
244 if (leadingZeroes <= 0) |
|
245 Ij.Copy(result->Right(bytesToPreserve)); |
|
246 else |
|
247 { |
|
248 Ij.FillZ(leadingZeroes); |
|
249 Ij.Append(*result); |
|
250 } |
|
251 |
|
252 CleanupStack::PopAndDestroy(3, &RI_Ij); // result, ciB, ciIj |
|
253 } |
|
254 |
|
255 #ifdef _DEBUG |
|
256 |
|
257 void PKCS12KDF::Panic(PKCS12KDF::TPanic aPanic) |
|
258 /** |
|
259 This function is used in debug builds to halt |
|
260 the current thread when a logic error is detected. |
|
261 |
|
262 The current thread is panicked with category "PKCS12KDF" |
|
263 and the supplied reason. |
|
264 |
|
265 @param aPanic Converted to numeric value and |
|
266 used for the panic reason. |
|
267 */ |
|
268 { |
|
269 _LIT(KPanicCat, "PKCS12KDF"); |
|
270 User::Panic(KPanicCat, aPanic); |
|
271 } |
|
272 |
|
273 #endif |
|
274 |